在大多数问题中,我看到了一个简单类的包装,该类没有构造函数,继承关系,只是调用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.
感谢您的帮助。
答案 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
保证仅支持两种语言链接:
- “ C ++”,默认的语言链接。
- “ 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);
}
}
如果接口要求调用方分配内存,则调用方需要知道要分配多少内存,因此一种方法是使函数返回所需的大小。
然后必须使用“ 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;