Desenvolvendo API de wrapper C para código C ++ orientado a object

Eu estou olhando para desenvolver um conjunto de APIs C que irá envolver nossas APIs C ++ existentes para acessar nossa lógica principal (escrita em C ++ orientada a objects). Esta será essencialmente uma API de cola que permite que nossa lógica C ++ possa ser usada por outras linguagens. Quais são alguns bons tutoriais, livros ou melhores práticas que introduzem os conceitos envolvidos no envolvimento do C em torno do C ++ orientado a objects?

    Isso não é muito difícil de fazer à mão, mas dependerá do tamanho da sua interface. Os casos em que fiz isso foram para permitir o uso de nossa biblioteca C ++ a partir do código C puro, e assim o SWIG não ajudou muito. (Bem, talvez SWIG pode ser usado para fazer isso, mas eu não sou guru SWIG e parecia não-trivial)

    Tudo o que acabamos fazendo foi:

    1. Todo object é passado em C um identificador opaco.
    2. Construtores e destruidores são envolvidos em funções puras
    3. Funções de membro são funções puras.
    4. Outros builtins são mapeados para C equivalentes, quando possível.

    Então, uma class como essa (header C ++)

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

    Mapearia para uma interface C assim (header 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 ); 

    A implementação da interface ficaria assim (fonte C ++)

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

    Derivamos nossa alça opaca da class original para evitar a necessidade de qualquer vazamento, e (isso não parece funcionar com o meu complium atual). Temos que fazer o manipulador de uma estrutura como C não suporta classs.

    Então isso nos dá a interface C básica. Se você quiser um exemplo mais completo mostrando uma maneira de integrar o tratamento de exceções, tente meu código no github: https://gist.github.com/mikeando/5394166

    A parte divertida agora é garantir que você obtenha todas as bibliotecas C ++ necessárias vinculadas a sua biblioteca maior corretamente. Para o gcc (ou clang), isso significa apenas fazer o estágio final do link usando g ++.

    Acho que a resposta de Michael Anderson está no caminho certo, mas minha abordagem seria diferente. Você precisa se preocupar com uma coisa extra: exceções. As exceções não fazem parte do C ABI, portanto, você não pode permitir que exceções sejam lançadas além do código C ++. Então seu header vai ficar assim:

     #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 

    E o arquivo .cpp do seu wrapper ficará assim:

     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( ms ); } void myStruct_destroy( void * v ) { MyStruct * ms = static_cast(v); delete ms; } int myStruct_doSomething( void * v, int i ) { MyStruct * ms = static_cast(v); int ret_value = -1; /* Assuming that a negative value means error */ try { ret_value = ms->doSomething(i); } catch (...) {} return ret_value; } 

    Melhor ainda: Se você sabe que tudo o que precisa como uma única instância do MyStruct, não corra o risco de lidar com pointers inválidos sendo passados ​​para sua API. Faça algo assim:

     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; } 

    Essa API é muito mais segura.

    Mas, como Michael mencionou, as ligações podem ficar bastante complicadas.

    Espero que isto ajude

    Não é difícil expor o código C ++ para C, basta usar o padrão de design Fachada

    Eu estou supondo que seu código C ++ é construído em uma biblioteca, tudo que você precisa fazer é fazer um módulo C em sua biblioteca C + + como uma fachada para sua biblioteca, juntamente com um arquivo de header C puro. O módulo C irá chamar as funções relevantes do C ++

    Depois disso, seus aplicativos e biblioteca C terão access total ao C api exposto.

    por exemplo, aqui está um módulo de Fachada de amostra

     #include  #include  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); } 

    você então expõe esta function C como sua API e você pode usá-la livremente como um C lib sem se preocupar

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

    Obviamente, este é um exemplo artificial, mas esta é a maneira mais fácil de expor uma biblioteca C ++ para C

    Eu acho que você pode ser capaz de obter algumas idéias sobre direção e / ou possivelmente utilizar diretamente o SWIG . Eu pensaria que passar por cima de alguns dos exemplos, pelo menos, lhe daria uma idéia de que tipos de coisas a considerar ao envolver uma API em outra. O exercício pode ser benéfico.

    SWIG é uma ferramenta de desenvolvimento de software que conecta programas escritos em C e C ++ com uma variedade de linguagens de programação de alto nível. O SWIG é usado com diferentes tipos de linguagens, incluindo linguagens de script comuns, como Perl, PHP, Python, Tcl e Ruby. A lista de idiomas suportados também inclui linguagens não scripting, como C #, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Lua, Modula-3, OCAML, Octave e R. Também várias implementações de Scheme interpretadas e compiladas ( Guile, MzScheme, Chicken) são suportados. SWIG é mais comumente usado para criar ambientes de programação interpretados ou compilados de alto nível, interfaces de usuário e como uma ferramenta para testar e prototipar o software C / C ++. O SWIG também pode exportar sua tree de análise na forma de expressões XML e Lisp. SWIG pode ser livremente utilizado, distribuído e modificado para uso comercial e não comercial.

    Basta replace o conceito de um object por um void * (geralmente chamado de tipo opaco em bibliotecas orientadas a C) e reutilizar tudo o que você sabe de C ++.

    Eu acho que usar o SWIG é a melhor resposta … não apenas evitar reinventar a roda, mas é confiável e também promover uma continuidade no desenvolvimento, em vez de triggersr o problema.

    Problemas de alta frequência precisam ser resolvidos por uma solução de longo prazo.