为面向对象的C ++代码开发C封装器API

我正在寻找开发一组C API,将围绕我们现有的C ++ API来访问我们的核心逻辑(用面向对象的C ++编写)。 这实质上是一个粘合API,允许我们的C ++逻辑可以被其他语言使用。 什么是一些很好的教程,书籍或最佳实践,介绍围绕面向对象的C ++包装C的概念?

手工操作不难,但取决于界面的大小。 我这样做的情况是在纯C代码中使用我们的C ++库,因此SWIG没有什么帮助。 (也许SWIG可以用来做到这一点,但我不是SWIG的大师,这似乎并不重要)

我们最终做的是:

  1. 每个对象都在C中传递一个不透明的句柄。
  2. 构造函数和析构函数被包装在纯函数中
  3. 成员函数是纯函数。
  4. 其他内build映射到可能的C等价物。

所以像这样的类(C ++头)

class MyClass { public: explicit MyClass( std::string & s ); ~MyClass(); int doSomething( int j ); } 

将映射到这样的C接口(C头):

 struct HMyClass; // An opaque type that we'll use as a handle typedef struct HMyClass HMyClass; HMyClass * myStruct_create( const char * s ); void myStruct_destroy( HMyClass * v ); int myStruct_doSomething( HMyClass * v, int i ); 

接口的实现看起来像这样(C ++源代码)

 #include "MyClass.h" extern "C" { HMyClass * myStruct_create( const char * s ) { return reinterpret_cast<HMyClass*>( new MyClass( s ) ); } void myStruct_destroy( HMyClass * v ) { delete reinterpret_cast<MyClass*>(v); } int myStruct_doSomething( HMyClass * v, int i ) { return reinterpret_cast<MyClass*>(v)->doSomething(i); } } 

我们从原来的类中派生出不透明的句柄,以避免需要任何投射, (这似乎不适用于我目前的编译器)。 我们必须使句柄成为一个结构,因为C不支持类。

这给了我们基本的C接口。 如果你想要一个更完整的例子来展示你可以集成exception处理的方法,那么你可以在github上试试我的代码: https : //gist.github.com/mikeando/5394166

有趣的部分是现在确保你得到所有必要的C ++库正确链接到你的大型库。 对于gcc(或铛),这意味着只使用g ++进行最后的链接阶段。

我认为迈克尔·安德森的答案是正确的,但我的做法会有所不同。 你不得不担心一件额外的事情:例外。 exception不是C ABI的一部分,所以你不能让exception抛出C ++代码。 所以你的头像是这样的:

 #ifdef __cplusplus extern "C" { #endif void * myStruct_create( const char * s ); void myStruct_destroy( void * v ); int myStruct_doSomething( void * v, int i ); #ifdef __cplusplus } #endif 

你的包装的.cpp文件将如下所示:

 void * myStruct_create( const char * s ) { MyStruct * ms = NULL; try { /* The constructor for std::string may throw */ ms = new MyStruct(s); } catch (...) {} return static_cast<void*>( ms ); } void myStruct_destroy( void * v ) { MyStruct * ms = static_cast<MyStruct*>(v); delete ms; } int myStruct_doSomething( void * v, int i ) { MyStruct * ms = static_cast<MyStruct*>(v); int ret_value = -1; /* Assuming that a negative value means error */ try { ret_value = ms->doSomething(i); } catch (...) {} return ret_value; } 

甚至更好:如果你知道所有你需要作为MyStruct的单个实例,不要冒着处理传递给你的API的void指针的风险。 做这样的事情,而不是:

 static MyStruct * _ms = NULL; int myStruct_create( const char * s ) { int ret_value = -1; /* error */ try { /* The constructor for std::string may throw */ _ms = new MyStruct(s); ret_value = 0; /* success */ } catch (...) {} return ret_value; } void myStruct_destroy() { if (_ms != NULL) { delete _ms; } } int myStruct_doSomething( int i ) { int ret_value = -1; /* Assuming that a negative value means error */ if (_ms != NULL) { try { ret_value = _ms->doSomething(i); } catch (...) {} } return ret_value; } 

这个API比较安全。

但是,正如迈克尔所说,链接可能会非常棘手。

希望这可以帮助

我想你可能会得到一些关于方向的想法和/或可能直接利用SWIG 。 我认为,通过几个例子,至less可以让你了解在将一个API包装到另一个时需要考虑的事情。 这个练习可能是有益的。

SWIG是一个软件开发工具,它将用C和C ++编写的程序与各种高级编程语言连接起来。 SWIG与不同types的语言一起使用,包括Perl,PHP,Python,Tcl和Ruby等通用脚本语言。 支持的语言列表还包括非脚本语言,如C#,Common Lisp(CLISP,Allegro CL,CFFI,UFFI),Java,Lua,Modula-3,OCAML,Octave和R.还有一些解释和编译的Scheme实现Guile,MzScheme,Chicken)都支持。 SWIG最常用于创build高级解释或编译的编程环境,用户界面以及用于testing和build立C / C ++软件的工具。 SWIG还可以以XML和Lisp sexpression式的forms导出它的分析树。 SWIG可以自由使用,分发和修改用于商业和非商业用途。

将C ++代码暴露给C并不难,只需使用Facadedevise模式即可

我假设你的C ++代码被内置到一个库中,你所需要做的就是在你的C ++库中创build一个C模块作为你的库的Facade和一个纯C头文件。 C模块将调用相关的C ++函数

一旦你这样做了,你的C应用程序和库将可以完全访问你暴露的C api。

例如,这里是一个示例Facade模块

 #include <libInterface.h> #include <objectedOrientedCppStuff.h> int doObjectOrientedStuff(int *arg1, int arg2, char *arg3) { Object obj = ObjectFactory->makeCppObj(arg3); // doing object oriented stuff here obj->doStuff(arg2); return obj->doMoreStuff(arg1); } 

然后你将这个C函数作为你的API公开,你可以自由使用它作为一个C库而不必担心

 // file name "libIntrface.h" extern int doObjectOrientedStuff(int *, int, char*); 

显然这是一个人为的例子,但这是将C ++库展示给C的最简单的方法

只需用一个void * (通常在C面向库中被称为不透明types)replace对象的概念,并重用从C ++知道的所有东西。

我认为使用SWIG是最好的答案……不仅避免了重复发明,而且还是可靠的,并且促进了发展的连续性,而不是一个问题。

高频问题需要通过长期解决scheme来解决。