Développement de l’API wrapper C pour le code C ++ orienté object

Je cherche à développer un ensemble d’API C qui entoureront nos API C ++ existantes pour accéder à notre logique de base (écrite en C ++ orienté object). Ce sera essentiellement une API de colle qui permettra à notre logique C ++ d’être utilisable par d’autres langages. Quels sont les bons tutoriels, livres ou bonnes pratiques qui introduisent les concepts impliqués dans l’encapsulation du C ++ orienté object?

Ce n’est pas trop difficile à faire à la main, mais cela dépend de la taille de votre interface. Les cas où je l’ai fait étaient pour permettre l’utilisation de notre bibliothèque C ++ à partir de code C pur, et SWIG n’était donc pas d’une grande aide. (Eh bien peut-être que SWIG peut être utilisé pour faire cela, mais je ne suis pas un gourou SWIG et cela semblait non sortingvial)

Tout ce que nous avons fini par faire était:

  1. Chaque object est passé dans C un manche opaque.
  2. Les constructeurs et les destructeurs sont entourés de fonctions pures
  3. Les fonctions membres sont des fonctions pures.
  4. Les autres fonctions intégrées sont mappées aux équivalents C lorsque cela est possible.

Donc, une classe comme celle-ci (en-tête C ++)

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

Mapperait à une interface C comme celle-ci (en-tête 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 ); 

L’implémentation de l’interface ressemblerait à ceci (source 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); } } 

Nous tirons notre poignée opaque de la classe d’origine pour éviter d’avoir besoin de casting, et (Cela ne semble pas fonctionner avec mon complément actuel). Nous devons faire en sorte que le descripteur soit struct, car C ne supporte pas les classes.

Cela nous donne donc l’interface C basique. Si vous voulez un exemple plus complet montrant une façon d’intégrer la gestion des exceptions, vous pouvez essayer mon code sur github: https://gist.github.com/mikeando/5394166

La partie amusante est maintenant de s’assurer que vous obtenez correctement toutes les bibliothèques C ++ requirejses liées dans votre plus grande bibliothèque. Pour gcc (ou clang), cela signifie simplement faire le dernier lien en utilisant g ++.

Je pense que la réponse de Michael Anderson est sur la bonne voie mais mon approche serait différente. Vous devez vous soucier d’une chose supplémentaire: les exceptions. Les exceptions ne font pas partie de C ABI, vous ne pouvez donc pas laisser les exceptions dépasser le code C ++. Donc, votre en-tête va ressembler à ceci:

 #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 

Et le fichier .cpp de votre wrapper ressemblera à ceci:

 void * myStruct_create( const char * s ) { MyStruct * ms = NULL; try { /* The constructor for std::ssortingng 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; } 

Encore mieux: si vous savez que tout ce dont vous avez besoin en tant qu’instance unique de MyStruct, ne prenez pas le risque de traiter les pointeurs de vides transmis à votre API. Faites plutôt quelque chose comme ça:

 static MyStruct * _ms = NULL; int myStruct_create( const char * s ) { int ret_value = -1; /* error */ try { /* The constructor for std::ssortingng 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; } 

Cette API est beaucoup plus sûre.

Mais, comme Michael l’a mentionné, la liaison peut être assez compliquée.

J’espère que cela t’aides

Il n’est pas difficile d’exposer du code C ++ à C, il suffit d’utiliser le motif de conception Facade

Je suppose que votre code C ++ est intégré à une bibliothèque, il vous suffit de créer un module C dans votre bibliothèque C ++ en tant que façade dans votre bibliothèque, avec un fichier d’en-tête C pur. Le module C appellera les fonctions C ++ correspondantes

Une fois que vous faites cela, vos applications C et votre bibliothèque auront un access complet à C api que vous avez exposé.

Par exemple, voici un exemple de module de façade

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

vous exposez alors cette fonction C comme votre API et vous pouvez l’utiliser librement comme une librairie sans vous soucier de

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

Evidemment c’est un exemple artificiel mais c’est le moyen le plus simple d’exposer une bibliothèque C ++ à C

Je pense que vous pourriez être en mesure d’obtenir des idées sur la direction et / ou éventuellement utiliser directement SWIG . Je pense que passer en revue quelques exemples vous donnerait au moins une idée du genre de choses à prendre en compte lors de l’enrobage d’une API dans une autre. L’exercice pourrait être bénéfique.

SWIG est un outil de développement logiciel qui connecte des programmes écrits en C et C ++ avec divers langages de programmation de haut niveau. SWIG est utilisé avec différents types de langages, notamment les langages de script courants tels que Perl, PHP, Python, Tcl et Ruby. La liste des langues sockets en charge comprend également des langages non scriptants tels que C #, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Lua, Modula-3, OCAML, Octave et R. Plusieurs implémentations de schémas interprétées et compilées ( Guile, MzScheme, Chicken) sont pris en charge. SWIG est le plus souvent utilisé pour créer des environnements de programmation, des interfaces utilisateur et des outils de test et de prototypage de logiciels C / C ++ de haut niveau, interprétés ou compilés. SWIG peut également exporter son arbre d’parsing sous la forme de s-expressions XML et Lisp. SWIG peut être librement utilisé, dissortingbué et modifié à des fins commerciales et non commerciales.

Remplacez simplement le concept d’object par un void * (souvent appelé un type opaque dans les bibliothèques orientées C) et réutilisez tout ce que vous connaissez depuis C ++.

Je pense que l’utilisation de SWIG est la meilleure réponse… non seulement elle évite de réinventer la roue, mais elle est également fiable et favorise également la continuité du développement plutôt que la résolution du problème.

Les problèmes de haute fréquence doivent être résolus par une solution à long terme.