如何编写用于从C访问C ++类成员的包装程序(带有继承和构造函数)

时间:2019-01-22 09:47:35

标签: c++ c visual-c++ extern

在大多数问题中,我看到了一个简单类的包装,该类没有构造函数,继承关系,只是调用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 :(得分:2)

使代码客观化非常容易:

#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继承也要用UB)。

附言:我认为这与所提供链接中的答案没有什么不同。

答案 1 :(得分:0)

您想要的是“取消对象化”​​功能。

必须使用第一个参数void*在外部创建类中的每个公共函数,其余参数与成员函数中的相同。

这意味着您还必须为对象创建“构造函数”和“析构函数”。

这些功能有两种工作方式,取决于数据的存储位置或存储位置:调用方提供的内存或库(在"free store"上分配的内存)。

新功能需要做的第一件事是声明链接https://en.cppreference.com/w/cpp/language/language_linkage

  

保证仅支持两种语言链接:

     
      
  1. “ C ++”,默认的语言链接。
  2.   
  3. “ C”,这使它成为可能   链接到用C编程语言编写的函数,并在C ++程序中定义可以从用C编写的模块调用的函数。
  4.   

因此,在新标头中,当在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);
    }
}

呼叫者分配的内存

如果接口要求调用方分配内存,则调用方需要知道要分配多少内存,因此一种方法是使函数返回所需的大小。

然后必须使用“ placement new”构造方法来调用构造函数。

在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;