Как написать Wrapper для доступа к члену класса C ++ из C (с наследованием и конструктором) - PullRequest
0 голосов
/ 22 января 2019

В большинстве вопросов я вижу оболочку для простого класса без конструктора, наследования и просто вызова void* для создания и уничтожения и функции foo.

Для структуры, подобной приведенной ниже, как создать оболочку для доступа к члену класса из C кода.

myHeader.h for c++
-------------------
class childA:public parentA {private: void logger() override}

class childB:public parentB 
{ 
  private: /*some members*/ 
  protected: /*some members*/
  public: 
      explicit childB(childA* a);
}
class parentB
{
  protected:
      MyType object;
  public:
      boolean Init(MyType obj); /*the implmentation is object=obj*/
}

Теперь в коде C я хочу получить доступ к object. Как мне написать обертку для этого?

Object type is a function pointer => typedef S32(*IoFunc)(Msg&);
where S32 is unsigned int, Msg is a struct.

Спасибо за вашу помощь.

Ответы [ 2 ]

0 голосов
/ 22 января 2019

Снять объект без кода довольно просто:

#ifdef __cplusplus
extern "C"
{
#endif
    void* construct_me(/*arguments*/);
    void* get_object(void* obj);
    void delete_me(void* obj);
#ifdef __cplusplus
}
#endif

И затем определить их:

extern "C"
{
    void* construct_me(/*arguments*/)
    {
        return static_cast<void*>(new parentB(/*arguments*/));
    }
    void* get_object(void* obj)
    {
        return static_cast<void*>(&(static_cast<parentB*>(obj)->object));
    }
    void delete_me(void* obj)
    {
        delete static_cast<parentB*>(obj);
    }
}

Если тип можно использовать в C, то вы можете просто сделать:

Type get_object(void* obj)
{
    return static_cast<parentB*>(obj)->object;
}

вместо приведения его к void*.

Наследование ничего не меняет.Это тот же механизм, за исключением того, что если у вас есть виртуальные функции, вы все равно должны обернуть их все для унаследованного класса (это UB, чтобы преобразовать A * в void * в B *, даже если A наследуется от B).

PS: я не думаю, что это отличается от ответов в приведенной ссылке.

0 голосов
/ 22 января 2019

То, что вы хотите, это «не объективировать» функции.

Каждая открытая функция, которая находится внутри класса, должна создаваться снаружи с первым параметром void*, а остальные параметры должны быть одинаковыми.как в функции-члене.

Это означает, что вам также нужно создать функцию «конструктор» и «деструктор» для объекта.

Существует два способа работы этих функций в зависимости от того, где хранятся данные: память, предоставляемая вызывающим абонентом, или память, выделенная библиотекой (в «free store» ).

Первое, что нужно новым функциям - это объявить связь https://en.cppreference.com/w/cpp/language/language_linkage

Гарантируется, что поддерживаются только две языковые связи:

  1. "C ++", языковая связь по умолчанию.
  2. «C», что позволяет связывать с функциями, написанными на языке программирования C, и определять в программе на C ++ функции, которые можно вызывать из модулей, написанных на C.

Таким образом, в новом заголовке, когда он используется в C ++, должна быть объявлена ​​связь, но когда заголовок используется в C, связь должна быть удалена:

Так что этонеобходимо в начале:

#ifdef __cplusplus
extern "C"
{
#endif

и в конце:

#ifdef __cplusplus
}
#endif

Память, выделенная в «свободном хранилище».

ОбъявлениеФункции в заголовке в приведенном выше коде должны быть:

void* childA_construct(); // ChildA doesn't have and constructor paramters
void* childA_destruct();

void* childB_construct(void* ptr_childA);
void* childB_destruct();

void* parentB_construct(); // parentB doesn't have and constructor paramters
void* parentB_destruct();
bool  parentB_Init(struct MyType m);

Далее в файле реализации:

extern "C"
{
    void* childA_construct()
    {
        return static_cast< void* >(new childA());
    }
    void  childA_destruct(void* ptr_childA)
    {
        delete static_cast< childA* >(ptr_childA);
    }

    void* childB_construct(void* ptr_childA)
    {
        childA* a_ptr = static_cast< childA* >(ptr_childA);
        return static_cast< void* >(new childB(a_ptr));
    }
    void childB_destruct(void* ptr_childB)
    {
        delete static_cast< childB* >(ptr_childB);
    }

    void* parentB_construct()
    {
        return static_cast< void* >(new parentB());
    }
    void* parentB_destruct(void* ptr_parentB)
    {
        delete static_cast< parentB* >(ptr_parentB);
    }

    bool  parentB_Init(void* ptr_parentB, struct MyType mt)
    {
        parentB* ptr_pb = static_cast< parentB* >(ptr_parentB);
        return ptr_pb->Init(mt);
    }
}

Память, выделенная вызывающей стороной

ЕслиИнтерфейс требует, чтобы вызывающая сторона выделяла память, а затем вызывающая сторона должна знать, какой объем памяти выделить, поэтому один из способов - заставить функцию вернуть требуемый размер.

Затем в методе конструкции «размещение нового» необходимоиспользоваться для вызова конструктора.

Находясь в функции destruct, деструктор должен вызываться вручную.

extern "C"
{
    int sizeof_childA() { return sizeof(childA); }

    void childA_construct2(void* ptr_buffer) { new (ptr_buffer)childA(/*constructor params*/); }
    void childA_destruct2(void* ptr_buffer) { static_cast< childA* >(ptr_buffer)->~childA(); }
}

Если вы хотите сохранить и использовать указатель функции для C, то для объявления типа функции:

extern "C" typedef unsigned MyFuncType(struct Msg*);

тогда переменная может храниться как:

MyFuncType func;
...