为面向对象的 C++ 代码开发 C 包装器 API
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2045774/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Developing C wrapper API for Object-Oriented C++ code
提问by theactiveactor
I'm looking to develop a set of C APIs that will wrap around our existing C++ APIs to access our core logic (written in object-oriented C++). This will essentially be a glue API that allows our C++ logic to be usable by other languages. What are some good tutorials, books, or best-practices that introduce the concepts involved in wrapping C around object-oriented C++?
我希望开发一组 C API,这些 API 将环绕我们现有的 C++ API 以访问我们的核心逻辑(用面向对象的 C++ 编写)。这本质上是一个胶水 API,它允许我们的 C++ 逻辑被其他语言使用。有哪些好的教程、书籍或最佳实践介绍了将 C 封装在面向对象的 C++ 中所涉及的概念?
采纳答案by Michael Anderson
This is not too hard to do by hand, but will depend on the size of your interface. The cases where I've done it were to enable use of our C++ library from within pure C code, and thus SWIG was not much help. (Well maybe SWIG can be used to do this, but I'm no SWIG guru and it seemed non-trivial)
这不是很难手工完成,但将取决于您的界面的大小。我所做的案例是在纯 C 代码中启用我们的 C++ 库,因此 SWIG 没有太大帮助。(好吧,也许 SWIG 可以用来做到这一点,但我不是 SWIG 大师,这似乎很重要)
All we ended up doing was:
我们最终做的就是:
- Every object is passed about in C an opaque handle.
- Constructors and destructors are wrapped in pure functions
- Member functions are pure functions.
- Other builtins are mapped to C equivalents where possible.
- 每个对象都在 C 中传递一个不透明的句柄。
- 构造函数和析构函数被包裹在纯函数中
- 成员函数是纯函数。
- 其他内置函数在可能的情况下映射到 C 等效项。
So a class like this (C++ header)
所以像这样的类(C++ 头文件)
class MyClass
{
public:
explicit MyClass( std::string & s );
~MyClass();
int doSomething( int j );
}
Would map to a C interface like this (C header):
将映射到这样的 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 );
The implementation of the interface would look like this (C++ source)
接口的实现看起来像这样(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);
}
}
We derive our opaque handle from the original class to avoid needing any casting, and (This didn't seem to work with my current complier). We have to make the handle a struct as C doesn't support classes.
我们从原始类派生我们的不透明句柄以避免需要任何强制转换,并且(这似乎不适用于我当前的编译器)。我们必须使句柄成为一个结构体,因为 C 不支持类。
So that gives us the basic C interface. If you want a more complete example showing one way that you can integrate exception handling, then you can try my code on github : https://gist.github.com/mikeando/5394166
这样我们就得到了基本的 C 接口。如果您想要一个更完整的示例来展示您可以集成异常处理的一种方式,那么您可以在 github 上尝试我的代码:https: //gist.github.com/mikeando/5394166
The fun part is now ensuring that you get all the required C++ libraries linked into you larger library correctly. For gcc (or clang) that means just doing the final link stage using g++.
现在有趣的部分是确保您将所有必需的 C++ 库正确链接到更大的库中。对于 gcc(或 clang),这意味着只使用 g++ 进行最后的链接阶段。
回答by figurassa
I think Michael Anderson's answer is on the right track but my approach would be different. You have to worry about one extra thing: Exceptions. Exceptions are not part of the C ABI so you cannot let Exceptions ever be thrown past the C++ code. So your header is going to look like this:
我认为迈克尔安德森的答案是正确的,但我的方法会有所不同。您必须担心一件额外的事情:异常。异常不是 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
And your wrapper's .cpp file will look like this:
您的包装器的 .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;
}
Even better: If you know that all you need as a single instance of MyStruct, don't take the risk of dealing with void pointers being passed to your API. Do something like this instead:
更好的是:如果您知道作为 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;
}
This API is a lot safer.
这个 API 安全得多。
But, as Michael mentioned, linking may get pretty tricky.
但是,正如迈克尔所说,链接可能会变得非常棘手。
Hope this helps
希望这可以帮助
回答by hhafez
It is not hard to expose C++ code to C, just use the Facade design pattern
将 C++ 代码暴露给 C 并不难,只要使用 Facade 设计模式
I am assuming your C++ code is built into a library, all you need to do is make one C module in your C++ library as a Facade to your library along with a pure C header file. The C module will call the relevant C++ functions
我假设您的 C++ 代码内置在一个库中,您需要做的就是在您的 C++ 库中创建一个 C 模块作为您库的 Facade 以及一个纯 C 头文件。C 模块会调用相关的 C++ 函数
Once you do that your C applications and library will have full access to the C api you exposed.
一旦你这样做了,你的 C 应用程序和库就可以完全访问你公开的 C api。
for example, here is a sample Facade module
例如,这是一个示例 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);
}
you then expose this C function as your API and you can use it freely as a C lib with out worrying about
然后,您将这个 C 函数公开为您的 API,您可以自由地将它用作 C 库,而无需担心
// file name "libIntrface.h"
extern int doObjectOrientedStuff(int *, int, char*);
Obviously this is a contrived example but this is the easiest way to expos a C++ library to C
显然这是一个人为的例子,但这是将 C++ 库暴露给 C 的最简单方法
回答by harschware
I would think you may be able to get some ideas on direction and/or possibly utilize directly SWIG. I would think that going over a few of the examples would at least give you an idea of what kinds of things to consider when wrapping one API into another. The exercise could be beneficial.
我认为您可以在方向上获得一些想法和/或可能直接使用SWIG。我认为阅读一些示例至少可以让您了解在将一个 API 包装到另一个 API 时需要考虑哪些类型的事情。锻炼可能是有益的。
SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages. SWIG is used with different types of languages including common scripting languages such as Perl, PHP, Python, Tcl and Ruby. The list of supported languages also includes non-scripting languages such as C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Lua, Modula-3, OCAML, Octave and R. Also several interpreted and compiled Scheme implementations (Guile, MzScheme, Chicken) are supported. SWIG is most commonly used to create high-level interpreted or compiled programming environments, user interfaces, and as a tool for testing and prototyping C/C++ software. SWIG can also export its parse tree in the form of XML and Lisp s-expressions. SWIG may be freely used, distributed, and modified for commercial and non-commercial use.
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 可以自由使用、分发、
回答by Hassan Syed
Just replace the concept of an object with a void *(often referred to as an opaque type in C oriented libraries) and reuse everything you know from C++.
只需将对象的概念替换为 a void *(在面向 C 的库中通常称为不透明类型)并重用您从 C++ 中知道的所有内容。
回答by danbo
I think using SWIG is the best answer... not only it avoid reinventing wheel but it is reliable and also promote a continuity in development rather than one shooting the problem.
我认为使用 SWIG 是最好的答案......不仅可以避免重新发明轮子,而且它是可靠的,并且还促进了开发的连续性,而不是解决问题。
High frequency problems need to be addressed by a long term solution.
高频问题需要通过长期解决方案来解决。

