Estoy buscando desarrollar un conjunto de API de C que se adapte a nuestras API de C ++ existentes para acceder a nuestra lógica central (escrita en C ++ orientado a objetos). Básicamente, será una API adhesiva que permitirá que otros lenguajes puedan utilizar nuestra lógica C ++. ¿Cuáles son algunos buenos tutoriales, libros o mejores prácticas que presentan los conceptos involucrados en envolver C alrededor de C ++ orientado a objetos?
81
Respuestas:
Esto no es demasiado difícil de hacer a mano, pero dependerá del tamaño de su interfaz. Los casos en los que lo hice fueron para permitir el uso de nuestra biblioteca C ++ desde el código C puro y, por lo tanto, SWIG no fue de mucha ayuda. (Bueno, tal vez SWIG pueda usarse para hacer esto, pero no soy un gurú de SWIG y no parecía trivial)
Todo lo que terminamos haciendo fue:
Entonces, una clase como esta (encabezado C ++)
class MyClass { public: explicit MyClass( std::string & s ); ~MyClass(); int doSomething( int j ); }
Se asignaría a una interfaz C como esta (encabezado 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 );
La implementación de la interfaz se vería así (fuente 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); } }
Derivamos nuestro identificador opaco de la clase original para evitar la necesidad de conversión, y(Esto no parece funcionar con mi compilador actual). Tenemos que convertir el identificador en una estructura, ya que C no admite clases.Eso nos da la interfaz C básica. Si desea un ejemplo más completo que muestre una forma en que puede integrar el manejo de excepciones, puede probar mi código en github: https://gist.github.com/mikeando/5394166
La parte divertida ahora es asegurarse de que obtenga todas las bibliotecas de C ++ necesarias vinculadas correctamente a su biblioteca más grande. Para gcc (o clang) eso significa simplemente hacer la etapa de enlace final usando g ++.
fuente
myStruct_destroy
ymyStruct_doSomething
. Debería serreinterpret_cast<MyClass*>(v)
.Creo que la respuesta de Michael Anderson está en el camino correcto, pero mi enfoque sería diferente. Tienes que preocuparte por una cosa más: Excepciones. Las excepciones no forman parte de la ABI de C, por lo que no puede permitir que las excepciones pasen del código C ++. Entonces, su encabezado se verá así:
#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
Y el archivo .cpp de su contenedor se verá así:
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; }
Aún mejor: si sabe que todo lo que necesita es una única instancia de MyStruct, no se arriesgue a lidiar con punteros nulos que se pasen a su API. En su lugar, haz algo como esto:
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; }
Esta API es mucho más segura.
Pero, como mencionó Michael, la vinculación puede volverse bastante complicada.
Espero que esto ayude
fuente
No es difícil exponer el código C ++ a C, solo use el patrón de diseño Facade
Supongo que su código C ++ está integrado en una biblioteca, todo lo que necesita hacer es crear un módulo C en su biblioteca C ++ como una fachada a su biblioteca junto con un archivo de encabezado C puro. El módulo C llamará a las funciones relevantes de C ++
Una vez que lo haga, sus aplicaciones y biblioteca C tendrán acceso completo a la API de C que expuso.
por ejemplo, aquí hay un módulo de fachada de muestra
#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); }
luego expone esta función C como su API y puede usarla libremente como una C lib sin preocuparse por
// file name "libIntrface.h" extern int doObjectOrientedStuff(int *, int, char*);
Obviamente, este es un ejemplo artificial, pero esta es la forma más fácil de exponer una biblioteca C ++ a C
fuente
Creo que podría obtener algunas ideas sobre la dirección y / o posiblemente utilizar directamente SWIG . Creo que repasar algunos de los ejemplos al menos te daría una idea de qué tipo de cosas debes considerar al encapsular una API en otra. El ejercicio puede resultar beneficioso.
fuente
Simplemente reemplace el concepto de un objeto con un
void *
(a menudo denominado tipo opaco en las bibliotecas orientadas a C) y reutilice todo lo que sabe de C ++.fuente
Creo que usar SWIG es la mejor respuesta ... no solo evita reinventar la rueda, sino que es confiable y también promueve una continuidad en el desarrollo en lugar de solucionar el problema.
Los problemas de alta frecuencia deben abordarse mediante una solución a largo plazo.
fuente