我正在寻找开发一组C API,它们将围绕我们现有的C ++ API来访问我们的核心逻辑(用面向对象的C ++编写)。这基本上是一个粘合API,允许我们的C ++逻辑可以被其他语言使用。什么是一些很好的教程,书籍或最佳实践,介绍了围绕面向对象的C ++包装C所涉及的概念?
答案 0 :(得分:63)
手动操作并不难,但取决于界面的大小。 我完成它的情况是允许在纯C代码中使用我们的C ++库,因此SWIG没有太大帮助。 (好吧也许SWIG可以用来做到这一点,但我不是SWIG大师,它似乎并非无足轻重)
我们最终做的就是:
这样的类(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);
}
}
我们从原始类派生我们的opaque句柄以避免需要任何转换,并且(这似乎不适用于我当前的编译器)。我们必须使句柄成为结构,因为C不支持类。
这样就为我们提供了基本的C接口。如果您想要一个更完整的示例,显示可以集成异常处理的一种方法,那么您可以在github上尝试我的代码:https://gist.github.com/mikeando/5394166
现在,有趣的部分是确保您正确地将所有必需的C ++库链接到更大的库中。对于gcc(或clang),这意味着只使用g ++进行最后的链接阶段。
答案 1 :(得分:14)
我认为迈克尔安德森的答案是正确的,但我的方法会有所不同。你不得不担心一件事:例外。异常不是C ABI的一部分,因此您不能让Exceptions超越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更安全。
但是,正如迈克尔所说,链接可能会非常棘手。
希望这有帮助
答案 2 :(得分:9)
将C ++代码公开给C并不难,只需使用Facade设计模式
即可我假设您的C ++代码已内置到库中,您只需将C ++库中的一个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 lib自由使用而无需担心
// file name "libIntrface.h"
extern int doObjectOrientedStuff(int *, int, char*);
显然这是一个人为的例子,但这是将C ++库暴露给C的最简单方法
答案 3 :(得分:6)
我认为你可能会对方向有所了解和/或可能直接使用SWIG。我认为,通过一些示例,至少可以让您了解将一个API包装到另一个API时需要考虑的事项。这项练习可能是有益的。
SWIG是一个软件开发工具,它将用C和C ++编写的程序与各种高级编程语言连接起来。 SWIG与不同类型的语言一起使用,包括常见的脚本语言,如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最常用于创建高级解释或编译的编程环境,用户界面,以及作为测试和原型化C / C ++软件的工具。 SWIG还可以以XML和Lisp s表达式的形式导出其解析树。 SWIG可以免费使用,分发和修改,用于商业和非商业用途。
答案 4 :(得分:4)
只需用void *
替换对象的概念(通常在面向C的库中称为opaque类型),并重用从C ++中知道的所有内容。
答案 5 :(得分:2)
我认为使用SWIG是最好的答案......它不仅可以避免重新发明轮子,而且可靠,并且可以促进发展的连续性,而不是一个人解决问题。
长期解决方案需要解决高频问题。