【发布时间】:2016-05-28 12:26:51
【问题描述】:
假设我们有一个 C++ 库,其类如下:
class TheClass {
public:
TheClass() { ... }
void magic() { ... }
private:
int x;
}
此类的典型用法包括堆栈分配:
TheClass object;
object.magic();
我们需要为这个类创建一个 C 包装器。最常见的方法如下所示:
struct TheClassH;
extern "C" struct TheClassH* create_the_class() {
return reinterpret_cast<struct TheClassH*>(new TheClass());
}
extern "C" void the_class_magic(struct TheClassH* self) {
reinterpret_cast<TheClass*>(self)->magic();
}
但是,它需要堆分配,这对于这么小的类来说显然是不希望的。
我正在寻找一种方法来允许从 C 代码中分配此类的堆栈。这是我能想到的:
struct TheClassW {
char space[SIZEOF_THECLASS];
}
void create_the_class(struct TheClassW* self) {
TheClass* cpp_self = reinterpret_cast<TheClass*>(self);
new(cpp_self) TheClass();
}
void the_class_magic(struct TheClassW* self) {
TheClass* cpp_self = reinterpret_cast<TheClass*>(self);
cpp_self->magic();
}
很难将类的真实内容放在结构的字段中。我们不能只包含 C++ 头文件,因为 C 不会理解它,所以它需要我们编写兼容的 C 头文件。这并不总是可能的。我认为 C 库并不真正需要关心结构的内容。
这个包装器的用法如下:
TheClassW object;
create_the_class(&object);
the_class_magic(&object);
问题:
- 这种方法有什么危险或缺点吗?
- 是否有替代方法?
- 是否有任何现有的包装器使用这种方法?
【问题讨论】:
-
类对象是否在
magic()中进行任何内存分配?还是班级本身的大小是您唯一担心的分配? -
如果类在内部使用堆分配,那完全没问题(反正什么也做不了)。我只需要对类本身进行堆栈分配。
-
您可能希望将
static_assert(sizeof(TheClassW) == sizeof(TheClass))放在包装源文件中。 -
通常你还需要一个
destruct_the_object(struct TheClassW* self) { reinterpret_cast<TheClass*>(self)->~TheClass(); }。加上一些处理异常的政策,例如见Code reuse in exception handling。 -
您可以使用 alloca() 在 C 包装器中的堆栈上分配空间,然后将 new() 用于 C++ 端的类以在那里构造对象。
标签: c++ c api shared-libraries