Как обернуть метод класса C ++, имеющий параметр или возвращающий экземпляр другого класса C ++ в C? - PullRequest
4 голосов
/ 24 сентября 2019

У меня есть класс C ++ A, имеющий методы, в то время как некоторые из них используют экземпляр другого класса C ++ B в качестве параметра, а некоторые возвращают экземпляр другого класса C ++ C.Как правильно обернуть в язык C класс A и все его методы?

Я не думаю, что мой вопрос является дубликатом, так как все существующие примеры показывают оболочку C (заголовок и источник)для простого класса C ++ с методами, имеющими параметры или возвращаемые значения простого типа данных, например int, char* и т. д.

class A {
  public:
    A(int param1);
    int getValue();
  private:
    int value;
}

// method B::DoSomething() has a parameter of type (class) "A"
class B {
  public:
    void DoSomething(A param1);
}

// method C::SetSomething() returns an instance of class "A"
class C {
  public:
    A SetSomething(int value);
}

Я знаю, как написать оболочку языка C для класса A и его метод: использование конструкции extern "C" { ... }.

Но как это сделать для классов B и C и их методов?

Ответы [ 3 ]

1 голос
/ 25 сентября 2019

Общий способ - создать оболочку для каждого класса C ++ и метод для генерации указателей на эти оболочки, а другой - для их удаления.

extern "C" {

struct WA{
    void* self;
};

struct WA* new_A( int p1 ){
    WA* wa = new WA;
    wa->self = new A( p1 );
    return wa; 
}

void delete_A( WA* wa ){
    delete static_cast<A*>( wa->self );
    delete wa;
}

}

Затем вы преобразуете каждый метод каждого класса в функциюпринимая указатель на структуру в качестве первого аргумента.A::getValue становится

int A_getValue( WA* wa) {
    return static_cast<A*>( wa->self )->getValue();
}

Экземпляры могут передаваться через указатель, поэтому B::DoSomething становится

void B_DoSomething( WB *wb, WA *param1){
    static_cast<B*>( wb->self )->DoSomething( *static_cast<A*>( param1->self ) );
}

Аналогично, вы можете возвращать экземпляры по указателю.C::SetSomething затем становится

struct WA* C_SetSomething( WC* wc, int value ){
    A a = static_cast<C*>( wc->self )->SetSomething( value );
    return new_A( a.getValue() );
}

Вы должны быть очень осторожны с управлением памятью в C-коде.Смотри рабочий пример здесь .

0 голосов
/ 25 сентября 2019

Почти во всех случаях вы не можете .

Только если struct - это POD, что означает, что это тривиальный тип и тип стандартного макета, который не имеет-статические члены, которые сами не являются POD.

В основном это означает, что вы можете использовать только структуры, которые являются BLOB-объектами данных без дополнительного поведения, и что C "понимает" в равной степени как C ++.

Причина проста: C не имеет представления о семантике C ++.Например, если в вашем классе есть деструктор, кто будет его запускать?Или, если ваш класс выполняет некоторый код при копировании, как C может узнать об этом?

У вас есть два варианта:

  • Поделиться только этими типами.Если вы не уверены, является ли что-то POD или нет, подтвердите это (в C ++):

    static_assert(std::is_pod_v<T>);
    
  • Сохраняйте свои классы, но передавайте непрозрачные указатели на C. Поскольку вы передаете толькоуказатели, C ничего с ними не сделает.

Обратите внимание, что ваши примеры классов B и C являются , а не POD, поэтому вы не можете сделать этодля тех.

0 голосов
/ 24 сентября 2019

Я предлагаю преобразовать struct B и struct C в функции, поскольку они не имеют членов:

void B_DoSomething(A * p_param)
{
  //...
}

A C_SetSomething(int value)
{
  A variable;
  variable.value = value;
  return variable;
}

В качестве напоминания, вы должны предпочесть передавать структуры по указателю на языке Си.В противном случае компилятор будет генерировать копии (кроме случаев, когда компилятор может оптимизировать).

...