为 C++ 库编写 C 包装器?

Write C wrapper for C++ libraries?

C++11 有 sleep_for。但是 C 标准没有等价物。

我想我可以写一个包装器来从 C 中调用这样的 C++ 函数。

还有一些有用的库,例如 Boost Filesystem 和 Asio。这些比较复杂,但我也可以写包装器。

莫非是重新发明轮子?这会是个坏主意吗?

一个问题是,如果你想编写调用 Boost 函数的 C 包装器,如果抛出异常你会怎么做?你基本上必须在它到达 C 之前在调用者中吞下它,但是然后将它正确地报告给 C 有点尴尬并且有很多类型的异常。

通常人们在特别想要额外的可移植性或由于某种原因无法提供所有 C++ 依赖项时使用 C 而不是 C++。如果您想编写一个使用 boost::filesystem 的 C 程序,为什么不将其编译为 C++?您要编写的异常处理样板代码会少很多,出错的可能性也会减少。

zeroMQ 就是这样做的。该库是用 C++ 编写的,但是库 API 是一个 C 接口。原则上,您将 classes 隐藏在 object 大小的内存块中并传递 void* 。对于一些简单的功能,例如sleep_for,你可以只创建一个包装函数。在你的 header 中,声明

extern "C" {void sleep_for(int duration);}

在您的 cpp-file 中,根据 std::this_thread::sleep_for.

执行此操作
sleep_for(int duration) {std::this_thread::sleep_for( std::chrono::milliseconds(duration);}

对于classes,你需要C函数来创建一个新的object,销毁它,当然还有调用成员函数。对于 class A,这可能如下所示:

C header 文件 "A_C_API.h":

extern "C" {
     enum {SIZE_OF_A = 4};
     typedef char[SIZE_OF_A] a_class;
     a_class initA(void* a, int x);
     void destructA(void* a);
     int callGet(void *a);
}

C++ source file:

class A {
public:
    A(int i): x{i} {}
    int get() const {return x;}
private:
    int x;
};

static_assert( SIZE_OF_A == sizeof(A), "size mismatch!");

void initA(void* a, int x) {
    new (a) A(x);
}

void destructA(void* a) {
    static_cast<A*>(a)->~A();        
}

int callGet(void *a) {
    return static_cast<A*>(a)->get();
}

然后在您的 C 应用程序中,您可以

#include "A_C_API.h"
int main(int argc, char* argv[]) {
    a_class obj;
    initA(&obj);
    printf("%d", callGet(&obj) );
    destructA( &obj );
    return 0;
}

您将必须捕获异常并将它们转换为某些 C 错误处理。您还必须处理某些平台上的对齐问题,尤其是 SPARK 平台。为了简单起见,我省略了这个,但你可以看看 zeromq header 如何做到这一点。