Ich möchte eine Reihe von C-APIs entwickeln, die unsere vorhandenen C ++ - APIs umschließen, um auf unsere Kernlogik zuzugreifen (geschrieben in objektorientiertem C ++). Dies wird im Wesentlichen eine Kleber-API sein, mit der unsere C ++ - Logik von anderen Sprachen verwendet werden kann. Welche guten Tutorials, Bücher oder Best Practices stellen die Konzepte vor, mit denen C um objektorientiertes C ++ gewickelt wird?
81
Antworten:
Dies ist nicht allzu schwer von Hand zu tun, hängt jedoch von der Größe Ihrer Benutzeroberfläche ab. Die Fälle, in denen ich es getan habe, waren, die Verwendung unserer C ++ - Bibliothek aus reinem C-Code heraus zu ermöglichen, und daher war SWIG keine große Hilfe. (Nun, vielleicht kann SWIG verwendet werden, um dies zu tun, aber ich bin kein SWIG-Guru und es schien nicht trivial)
Am Ende haben wir nur Folgendes getan:
Also eine Klasse wie diese (C ++ - Header)
class MyClass { public: explicit MyClass( std::string & s ); ~MyClass(); int doSomething( int j ); }
Würde einer C-Schnittstelle wie dieser zugeordnet werden (C-Header):
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 );
Die Implementierung der Schnittstelle würde so aussehen (C ++ Quelle)
#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); } }
Wir leiten unseren undurchsichtigen Griff von der ursprünglichen Klasse ab, um zu vermeiden, dass ein Casting erforderlich ist, und(Dies schien mit meinem aktuellen Complier nicht zu funktionieren). Wir müssen das Handle zu einer Struktur machen, da C keine Klassen unterstützt.Das gibt uns also die grundlegende C-Schnittstelle. Wenn Sie ein vollständigeres Beispiel wünschen, das eine Möglichkeit zeigt, die Ausnahmebehandlung zu integrieren, können Sie meinen Code auf github ausprobieren: https://gist.github.com/mikeando/5394166
Der unterhaltsame Teil besteht nun darin, sicherzustellen, dass alle erforderlichen C ++ - Bibliotheken korrekt mit Ihrer größeren Bibliothek verknüpft sind. Für gcc (oder clang) bedeutet dies, dass nur die letzte Verbindungsphase mit g ++ durchgeführt wird.
quelle
myStruct_destroy
undmyStruct_doSomething
Funktionen. Sollte seinreinterpret_cast<MyClass*>(v)
.Ich denke, Michael Andersons Antwort ist auf dem richtigen Weg, aber mein Ansatz wäre anders. Sie müssen sich um eine zusätzliche Sache kümmern: Ausnahmen. Ausnahmen sind nicht Teil des C ABI, daher können Sie nicht zulassen, dass Ausnahmen jemals über den C ++ - Code hinausgeworfen werden. Ihr Header wird also so aussehen:
#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
Die CPP-Datei Ihres Wrappers sieht folgendermaßen aus:
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; }
Noch besser: Wenn Sie wissen, dass alles, was Sie als einzelne Instanz von MyStruct benötigen, nicht das Risiko eingeht, dass ungültige Zeiger an Ihre API übergeben werden. Mach stattdessen so etwas:
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; }
Diese API ist viel sicherer.
Aber, wie Michael erwähnte, kann das Verknüpfen ziemlich schwierig werden.
Hoffe das hilft
quelle
Es ist nicht schwer, C ++ - Code für C verfügbar zu machen. Verwenden Sie einfach das Facade-Entwurfsmuster
Ich gehe davon aus, dass Ihr C ++ - Code in eine Bibliothek integriert ist. Sie müssen lediglich ein C-Modul in Ihrer C ++ - Bibliothek als Fassade für Ihre Bibliothek zusammen mit einer reinen C-Header-Datei erstellen. Das C-Modul ruft die relevanten C ++ - Funktionen auf
Sobald Sie dies getan haben, haben Ihre C-Anwendungen und Ihre Bibliothek vollen Zugriff auf die von Ihnen bereitgestellte C-API.
Hier ist zum Beispiel ein Beispiel für ein Fassadenmodul
#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); }
Sie stellen diese C-Funktion dann als Ihre API zur Verfügung und können sie frei als C-Bibliothek verwenden, ohne sich Sorgen machen zu müssen
// file name "libIntrface.h" extern int doObjectOrientedStuff(int *, int, char*);
Natürlich ist dies ein erfundenes Beispiel, aber dies ist der einfachste Weg, eine C ++ - Bibliothek für C verfügbar zu machen
quelle
Ich würde denken, dass Sie in der Lage sein könnten, einige Ideen zur Richtung zu bekommen und / oder SWIG direkt zu nutzen . Ich würde denken, dass ein Blick auf einige Beispiele Ihnen zumindest eine Vorstellung davon geben würde, welche Dinge zu beachten sind, wenn Sie eine API in eine andere einbinden. Die Übung könnte von Vorteil sein.
quelle
Ersetzen Sie einfach das Konzept eines Objekts durch ein
void *
(in C-orientierten Bibliotheken häufig als undurchsichtiger Typ bezeichnet) und verwenden Sie alles, was Sie aus C ++ wissen, wieder.quelle
Ich denke, SWIG ist die beste Antwort ... es vermeidet nicht nur eine Neuerfindung des Rades, sondern ist auch zuverlässig und fördert eine Kontinuität in der Entwicklung, anstatt das Problem zu lösen.
Hochfrequenzprobleme müssen durch eine langfristige Lösung angegangen werden.
quelle