gpt4 book ai didi

c++ - 具有堆栈分配的 C++ 类的 C 包装器

转载 作者:IT老高 更新时间:2023-10-28 12:54:06 26 4
gpt4 key购买 nike

假设我们有一个 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);

问题:

  • 这种方法有什么危险或缺点吗?
  • 有其他方法吗?
  • 是否有任何现有的包装器使用这种方法?

最佳答案

您可以使用 placement new结合alloca在堆栈上创建一个对象。对于 Windows,有 _malloca .这里的重要性是 alloca 和 malloca 为您相应地对齐内存,并且包装 sizeof 运算符可移植地公开您的类的大小。请注意,在 C 代码中,当您的变量超出范围时不会发生任何事情。尤其是不会破坏您的对象。

main.c

#include "the_class.h"
#include <alloca.h>

int main() {
void *me = alloca(sizeof_the_class());

create_the_class(me, 20);

if (me == NULL) {
return -1;
}

// be aware return early is dangerous do
the_class_magic(me);
int error = 0;
if (error) {
goto fail;
}

fail:
destroy_the_class(me);
}

the_class.h

#ifndef THE_CLASS_H
#define THE_CLASS_H

#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
class TheClass {
public:
TheClass(int me) : me_(me) {}
void magic();
int me_;
};

extern "C" {
#endif

size_t sizeof_the_class();
void *create_the_class(void* self, int arg);
void the_class_magic(void* self);
void destroy_the_class(void* self);

#ifdef __cplusplus
}
#endif //__cplusplus


#endif // THE_CLASS_H

the_class.cc

#include "the_class.h"

#include <iostream>
#include <new>

void TheClass::magic() {
std::cout << me_ << std::endl;
}

extern "C" {
size_t sizeof_the_class() {
return sizeof(TheClass);
}

void* create_the_class(void* self, int arg) {
TheClass* ptr = new(self) TheClass(arg);
return ptr;
}

void the_class_magic(void* self) {
TheClass *tc = reinterpret_cast<TheClass *>(self);
tc->magic();
}

void destroy_the_class(void* self) {
TheClass *tc = reinterpret_cast<TheClass *>(self);
tc->~TheClass();
}
}

编辑:

您可以创建一个包装宏来避免创建和初始化的分离。你不能使用 do { } while(0) 风格的宏,因为它会限制变量的范围。还有其他方法可以解决这个问题,但这在很大程度上取决于您如何处理代码库中的错误。概念证明如下:

#define CREATE_THE_CLASS(NAME, VAL, ERR) \
void *NAME = alloca(sizeof_the_class()); \
if (NAME == NULL) goto ERR; \

// example usage:
CREATE_THE_CLASS(me, 20, fail);

这在 gcc 中扩展为:

void *me = __builtin_alloca (sizeof_the_class()); if (me == __null) goto fail; create_the_class(me, (20));;

关于c++ - 具有堆栈分配的 C++ 类的 C 包装器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35445883/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com