Chcę opracować zestaw interfejsów API C, które będą otaczać nasze istniejące interfejsy API C ++, aby uzyskać dostęp do naszej podstawowej logiki (napisanej w zorientowanym obiektowo C ++). Zasadniczo będzie to interfejs API kleju, który umożliwia korzystanie z naszej logiki C ++ w innych językach. Jakie są dobre samouczki, książki lub sprawdzone metody, które wprowadzają pojęcia związane z opakowaniem języka C wokół zorientowanego obiektowo języka C ++?
81
Odpowiedzi:
Nie jest to zbyt trudne do zrobienia ręcznie, ale zależy to od rozmiaru interfejsu. Przypadki, w których to zrobiłem, polegały na umożliwieniu korzystania z naszej biblioteki C ++ z poziomu czystego kodu C, a zatem SWIG nie był zbyt pomocny. (Cóż, może SWIG może być do tego użyty, ale nie jestem guru SWIG i wydawało się to nietrywialne)
Skończyło się na tym, że:
Czyli taka klasa (nagłówek C ++)
class MyClass { public: explicit MyClass( std::string & s ); ~MyClass(); int doSomething( int j ); }
Zmapowałoby na interfejs C w ten sposób (nagłówek 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 );
Implementacja interfejsu wyglądałaby następująco (źródło 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); } }
Wyprowadzamy nasz nieprzezroczysty uchwyt z oryginalnej klasy, aby uniknąć konieczności odlewania i(To nie wydawało się działać z moim obecnym komplementem). Musimy uczynić uchwyt strukturą, ponieważ C nie obsługuje klas.To daje nam podstawowy interfejs C. Jeśli potrzebujesz bardziej kompletnego przykładu pokazującego jeden sposób, w jaki możesz zintegrować obsługę wyjątków, możesz wypróbować mój kod na github: https://gist.github.com/mikeando/5394166
Zabawną częścią jest teraz upewnienie się, że wszystkie wymagane biblioteki C ++ są poprawnie połączone z większą biblioteką. W przypadku gcc (lub clang) oznacza to po prostu wykonanie ostatniego etapu linkowania za pomocą g ++.
źródło
myStruct_destroy
imyStruct_doSomething
funkcjach są dwie literówki . Powinien byćreinterpret_cast<MyClass*>(v)
.Myślę, że odpowiedź Michaela Andersona jest na dobrej drodze, ale moje podejście byłoby inne. Musisz się martwić o jedną dodatkową rzecz: wyjątki. Wyjątki nie są częścią C ABI, więc nie można pozwolić, aby wyjątki były kiedykolwiek wyrzucane poza kod C ++. Więc twój nagłówek będzie wyglądał tak:
#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
A plik .cpp twojego opakowania będzie wyglądał następująco:
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; }
Nawet lepiej: jeśli wiesz, że wszystko, czego potrzebujesz jako pojedyncza instancja MyStruct, nie ryzykujesz, że wskaźniki void zostaną przekazane do twojego API. Zamiast tego zrób coś takiego:
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; }
Ten interfejs API jest o wiele bezpieczniejszy.
Ale, jak wspomniał Michael, tworzenie linków może być dość trudne.
Mam nadzieję że to pomoże
źródło
Nie jest trudno udostępnić kod C ++ w C, po prostu użyj wzorca projektowego Facade
Zakładam, że twój kod C ++ jest wbudowany w bibliotekę, wszystko, co musisz zrobić, to utworzyć jeden moduł C w swojej bibliotece C ++ jako fasadę do swojej biblioteki wraz z czystym plikiem nagłówkowym C. Moduł C wywoła odpowiednie funkcje C ++
Gdy to zrobisz, Twoje aplikacje C i biblioteka będą miały pełny dostęp do udostępnionego interfejsu API języka C.
na przykład tutaj jest przykładowy moduł Fasada
#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); }
następnie ujawniasz tę funkcję C jako swoje API i możesz jej swobodnie używać jako biblioteki C bez martwienia się o
// file name "libIntrface.h" extern int doObjectOrientedStuff(int *, int, char*);
Oczywiście jest to wymyślony przykład, ale jest to najłatwiejszy sposób pokazania biblioteki C ++ w C
źródło
Myślę, że możesz mieć kilka pomysłów na kierunek i / lub być może bezpośrednio wykorzystać SWIG . Myślę, że przejrzenie kilku przykładów dałoby przynajmniej wyobrażenie o tym, jakie rzeczy należy wziąć pod uwagę podczas pakowania jednego interfejsu API do drugiego. Ćwiczenie może być korzystne.
źródło
Po prostu zastąp pojęcie obiektu znakiem
void *
(często określanym jako nieprzezroczysty typ w bibliotekach zorientowanych na C) i ponownie wykorzystaj wszystko, co znasz z C ++.źródło
Myślę, że użycie SWIG jest najlepszą odpowiedzią ... nie tylko pozwala uniknąć ponownego wynalezienia koła, ale jest niezawodne, a także promuje ciągłość rozwoju, a nie rozwiązywanie problemu.
Problemy związane z wysoką częstotliwością wymagają rozwiązania długoterminowego.
źródło