简体   繁体   English

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

[英]Developing C wrapper API for Object-Oriented C++ code

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++). 我正在寻求开发一组C API,这些C API将包装我们现有的C ++ API以访问我们的核心逻辑(以面向对象的C ++编写)。 This will essentially be a glue API that allows our C++ logic to be usable by other languages. 本质上,这将是一个胶粘API,它允许我们的C ++逻辑可被其他语言使用。 What are some good tutorials, books, or best-practices that introduce the concepts involved in wrapping C around object-oriented C++? 有什么好的教程,书籍或最佳实践介绍围绕面向对象的C ++包装C涉及的概念?

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. 我这样做的情况是允许在纯C代码中使用我们的C ++库,因此SWIG并没有太大帮助。 (Well maybe SWIG can be used to do this, but I'm no SWIG guru and it seemed non-trivial) (也许可以使用SWIG来做到这一点,但我不是SWIG专家,这似乎并不重要)

All we ended up doing was: 我们最终要做的是:

  1. Every object is passed about in C an opaque handle. 每个对象都在C中传递一个不透明的句柄。
  2. Constructors and destructors are wrapped in pure functions 构造函数和析构函数包装在纯函数中
  3. Member functions are pure functions. 成员函数是纯函数。
  4. Other builtins are mapped to C equivalents where possible. 在可能的情况下,其他内建映射到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. 这样就给了我们基本的C接口。 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 如果您想要一个更完整的示例来显示集成异常处理的一种方式,则可以在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. 现在,有趣的部分是确保您将所有必需的C ++库正确链接到较大的库中。 For gcc (or clang) that means just doing the final link stage using g++. 对于gcc(或clang),这意味着仅使用g ++进行最后的链接阶段。

I think Michael Anderson's answer is on the right track but my approach would be different. 我认为迈克尔·安德森(Michael Anderson)的答案是正确的,但我的做法会有所不同。 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. 异常不是C ABI的一部分,因此您不能让异常抛出C ++代码。 So your header is going to look like this: 所以您的标题将如下所示:

#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. 更好的是:如果您知道作为MyStruct的单个实例所需的全部,那么不要冒险将无效指针传递给您的API。 Do something like this instead: 做这样的事情:

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 希望这可以帮助

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. 我假设您的C ++代码已内置到库中,您需要做的就是在C ++库中创建一个C模块,作为库的外观和纯C头文件。 The C module will call the relevant C++ functions 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的最简单方法

I would think you may be able to get some ideas on direction and/or possibly utilize directly SWIG . 我认为您可能能够获得一些指导意见和/或直接利用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. 我认为,回顾一些示例至少可以使您了解将一个API封装到另一个API中要考虑的事情。 The exercise could be beneficial. 这项运动可能是有益的。

SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages. SWIG是一种软件开发工具,可将用C和C ++编写的程序与各种高级编程语言相连接。 SWIG is used with different types of languages including common scripting languages such as Perl, PHP, Python, Tcl and Ruby. SWIG与多种类型的语言一起使用,包括常见的脚本语言,例如Perl,PHP,Python,Tcl和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. 支持的语言列表还包括非脚本语言,例如C#,Common Lisp(CLISP,Allegro CL,CFFI,UFFI),Java,Lua,Modula-3,OCAML,Octave和R。还有几种解释和编译的Scheme实现(支持Guile,MzScheme,Chicken)。 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最常用于创建高级解释或编译的编程环境,用户界面,以及作为测试和原型C / C ++软件的工具。 SWIG can also export its parse tree in the form of XML and Lisp s-expressions. SWIG还可以XML和Lisp s表达式的形式导出其解析树。 SWIG may be freely used, distributed, and modified for commercial and non-commercial use. SWIG可以自由使用,分发和修改,以用于商业和非商业用途。

只需将对象的概念替换为void * (在面向C的库中通常称为不透明类型),然后重用您从C ++知道的一切。

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. 高频问题需要长期解决。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM