为 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 如何做到这一点。
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 如何做到这一点。