我正在寻求开发一组C API,这些C API将包装我们现有的C ++ API以访问我们的核心逻辑(以面向对象的C ++编写)。本质上,这将是一个胶粘API,它允许我们的C ++逻辑可被其他语言使用。有什么好的教程,书籍或最佳实践介绍围绕面向对象的C ++包装C涉及的概念?
我正在寻求开发一组C API,这些C API将包装我们现有的C ++ API以访问我们的核心逻辑(以面向对象的C ++编写)。本质上,这将是一个胶粘API,它允许我们的C ++逻辑可被其他语言使用。有什么好的教程,书籍或最佳实践介绍围绕面向对象的C ++包装C涉及的概念?
Answers:
手工很难做到这一点,但要取决于您界面的大小。我这样做的情况是允许在纯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);
}
}
我们从原始类派生不透明的句柄,以避免进行任何强制转换,并且(这似乎不适用于我当前的编译器)。由于C不支持类,我们必须将句柄设为结构。
这样就给了我们基本的C接口。如果您想要一个更完整的示例来显示集成异常处理的一种方式,则可以在github上尝试我的代码:https : //gist.github.com/mikeando/5394166
现在,有趣的部分是确保您将所有必需的C ++库正确链接到较大的库中。对于gcc(或clang),这意味着仅使用g ++进行最后的链接阶段。
myStruct_destroy
和myStruct_doSomething
函数中有两种错别字。应该是reinterpret_cast<MyClass*>(v)
。
我认为迈克尔·安德森(Michael Anderson)的答案是正确的,但我的做法会有所不同。您必须担心另外一件事:异常。异常不是C ABI的一部分,因此您不能让异常抛出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的风险。做这样的事情:
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更安全。
但是,正如迈克尔提到的那样,链接可能会非常棘手。
希望这可以帮助
将C ++代码公开给C并不难,只需使用Facade设计模式
我假设您的C ++代码已内置到库中,您需要做的就是在C ++库中创建一个C模块,作为与纯C头文件一起使用的库的Facade。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的最简单方法
我认为您可能能够获得一些指导思想和/或直接利用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可以自由使用,分发,
只需将对象的概念替换为void *
(在面向C的库中通常称为不透明类型),然后重用您从C ++知道的一切。