node c++
Desarrollar C wrapper API para código C++ orientado a objetos (6)
Creo que es posible que pueda obtener algunas ideas sobre la dirección y / o posiblemente utilizar directamente SWIG . Creo que revisar algunos de los ejemplos al menos te daría una idea de qué tipo de cosas considerar al envolver una API en otra. El ejercicio podría ser beneficioso.
SWIG es una herramienta de desarrollo de software que conecta programas escritos en C y C ++ con una variedad de lenguajes de programación de alto nivel. SWIG se utiliza con diferentes tipos de idiomas, incluidos lenguajes de scripting comunes como Perl, PHP, Python, Tcl y Ruby. La lista de idiomas admitidos también incluye lenguajes sin scripts como C #, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Lua, Modula-3, OCAML, Octave y R. También varias implementaciones del esquema interpretadas y compiladas ( Guile, MzScheme, Chicken) son compatibles. SWIG se utiliza con mayor frecuencia para crear entornos de programación interpretados o compilados de alto nivel, interfaces de usuario y como una herramienta para probar y crear prototipos de software C / C ++. SWIG también puede exportar su árbol de análisis sintáctico en forma de s-expresiones XML y Lisp. SWIG se puede usar, distribuir y modificar libremente para uso comercial y no comercial.
Estoy buscando desarrollar un conjunto de API de C que cubra nuestras API de C ++ existentes para acceder a nuestra lógica central (escrita en C ++ orientado a objetos). Esto será esencialmente una API de pegamento que permite que nuestra lógica C ++ pueda ser utilizada por otros lenguajes. ¿Cuáles son algunos buenos tutoriales, libros o mejores prácticas que introducen los conceptos involucrados en el ajuste de C alrededor de C ++ orientado a objetos?
Creo que la respuesta de Michael Anderson está en el camino correcto, pero mi enfoque sería diferente. Tienes que preocuparte por algo extra: Excepciones. Las excepciones no son parte de la C ABI, por lo que no puede dejar que las Excepciones lleguen al 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 envoltura 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;
}
Mejor aún: si sabe que todo lo que necesita como instancia única de MyStruct, no se arriesgue a tratar con punteros vacíos que pasan a su API. Haz algo como esto en su lugar:
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 Michael mencionó, la vinculación puede ser bastante complicada.
Espero que esto ayude
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 una sola toma del problema.
Los problemas de alta frecuencia deben abordarse mediante una solución a largo plazo.
Esto no es demasiado difícil de hacer a mano, pero dependerá del tamaño de su interfaz. Los casos donde lo hice fueron para permitir el uso de nuestra biblioteca de C ++ desde el código C puro, y por lo tanto, SWIG no fue de mucha ayuda. (Bueno, tal vez SWIG se puede usar para hacer esto, pero no soy un gurú de SWIG y me pareció no trivial)
Todo lo que terminamos haciendo fue:
- Cada objeto se pasa en C un mango opaco.
- Los constructores y los destructores están envueltos en funciones puras
- Las funciones miembro son funciones puras.
- Otros builtins se asignan a C equivalentes siempre que sea posible.
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 de 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 pareció funcionar con mi compilador actual). Tenemos que hacer que el manejador sea una estructura ya que C no admite clases.
Entonces 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 garantizar que obtenga correctamente todas las bibliotecas C ++ requeridas vinculadas a su biblioteca más grande. Para gcc (o clang) eso significa simplemente hacer la etapa de enlace final usando g ++.
No es difícil exponer el código C ++ a C, solo usa el patrón de diseño de Fachada
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 Fachada para 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 C y su biblioteca tendrán acceso completo a la API que expuso.
por ejemplo, aquí hay un módulo 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);
}
a continuación, expone esta función C como su API y puede usarla libremente como biblioteca en C 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
Simplemente reemplace el concepto de un objeto con un void *
(a menudo denominado tipo opaco en bibliotecas orientadas a C) y reutilice todo lo que conoce de C ++.