Su archivo de cabecera, que es compartido entre su código C y C++:
#ifdef __cplusplus // only actually define the class if this is C++
class some_class
{
public:
int some_method(float);
};
#else
// C doesn't know about classes, just say it's a struct
typedef struct some_class some_class;
#endif
// access functions
#ifdef __cplusplus
#define EXPORT_C extern "C"
#else
#define EXPORT_C
#endif
EXPORT_C some_class* some_class_new(void);
EXPORT_C void some_class_delete(some_class*);
EXPORT_C int some_class_some_method(some_class*, float);
A continuación, el archivo de origen:
#include "some_foo.h"
int some_class::some_method(float f)
{
return static_cast<int>(f);
}
// access functions
EXPORT_C some_class* some_class_new(void)
{
return new some_class();
}
EXPORT_C void some_class_delete(some_class* this)
{
delete this;
}
EXPORT_C int some_class_some_method(some_class* this, float f)
{
return this->some_method(f);
}
Ahora compila esa fuente, y enlazar con él. Su fuente de C sería algo así como:
#include "some_class.h"
some_class* myInstance = some_class_new();
int i = some_class_some_method(myInstance, 10.0f);
some_class_delete(myInstance);
Si usted es serio sobre la mezcla de C y C++, querrá de macro.
Éstos son algunos ejemplos de macro que haría esto mucho más fácil:
// in something like c_export.h
// extern "C" macro
#ifdef __cplusplus
#define EXPORT_C extern "C"
#else
#define EXPORT_C
#endif
// new
#define EXPORT_C_CLASS_NEW(classname) EXPORT_C \
classname * classname##_new(void)
#define EXPORT_C_CLASS_NEW_DEFINE(classname) \
EXPORT_C_CLASS_NEW(classname) \
{ return new classname(); }
// repeat as much as you want. allows passing parameters to the constructor
#define EXPORT_C_CLASS_NEW_1(classname, param1) EXPORT_C \
classname * classname##_new(param1 p1)
#define EXPORT_C_CLASS_NEW_1_DEFINE(classname, param1) \
EXPORT_C_CLASS_NEW_1(classname, param1) \
{ return new classname (p1); }
// delete
#define EXPORT_C_CLASS_DELETE(classname) EXPORT_C \
void classname##_delete(classname * this)
#define EXPORT_C_CLASS_DELETE_DEFINE(classname) \
EXPORT_C_CLASS_DELETE(classname) \
{ delete this; }
// functions
#define EXPORT_C_CLASS_METHOD(classname, methodname, ret) EXPORT_C \
ret classname##_##methodname##(classname * this)
#define EXPORT_C_CLASS_METHOD_DEFINE(classname, methodname, ret) \
EXPORT_C_CLASS_METHOD(classname, methodname, ret) \
{ return this->##methodname##(); }
// and repeat as necessary.
#define EXPORT_C_CLASS_METHOD_1(classname, methodname, ret, param1) EXPORT_C \
ret classname##_##methodname(classname * this, param1 p1)
#define EXPORT_C_CLASS_METHOD_1_DEFINE(classname, methodname, ret, param1) \
EXPORT_C_CLASS_METHOD_1(classname, methodname, ret, param1) \
{ return this->##methodname##(p1); }
Y así sucesivamente. Nuestro encabezado/fuente se convierte en:
// header
#include "c_export.h" // utility macros
#ifdef __cplusplus // only actually define the class if this is C++
class some_class
{
public:
int some_method(float);
};
#else
// C doesn't know about classes, just say it's a struct
typedef struct some_class some_class;
#endif
// access functions
EXPORT_C_CLASS_NEW(some_class);
EXPORT_C_CLASS_DELETE(some_class);
EXPORT_C_CLASS_METHOD_1(some_class, some_method, int, float);
// source
#include "some_foo.h"
int some_class::some_method(float f)
{
return static_cast<int>(f);
}
// access functions
EXPORT_C_CLASS_NEW_DEFINE(some_class);
EXPORT_C_CLASS_DELETE_DEFINE(some_class);
EXPORT_C_CLASS_METHOD_1_DEFINE(some_class, some_method, int, float);
Y eso es mucho más conciso. Podría hacerse más simple (posiblemente) con macro variadas, pero eso no es estándar y se lo dejo a usted. :] Además, puede crear macros para funciones normales que no son miembros.
Tenga en cuenta que C hace no saben lo que son las referencias. Si desea enlazar a una referencia, su mejor opción probablemente sea solo escribir la definición de exportación manualmente. (Pero lo pensaré, tal vez podamos obtenerlo automáticamente).
Imagine que nuestro some_class
tomó el float
por referencia (no const) (por cualquier razón). Definiríamos la función de esta manera:
// header
// pass by pointer! v
EXPORT_C_CLASS_METHOD_1(some_class, some_method, int, float*) ;
// source
EXPORT_C_CLASS_METHOD_1(some_class, some_method, int, float*)
{
// dereference pointer; now can be used as reference
return this->some_method(*p1);
}
Y ahí vamos. C podría interactuar con referencias con punteros en su lugar:
// c source, if some_method took a reference:
float f = 10.0f;
int i = some_class_some_method(myInstance, &f);
y pasamos f
"por referencia".
Si bien no hablé de la "C" externa, hay mucha información relevante en esta publicación sobre el acceso a las API de OO C++ desde C http://stackoverflow.com/questions/2045774/developing-c-wrapper-api -for-object-oriented-c-code/ –
@Peter: He arreglado las cosas externas de "C". El compilador de C no tiene idea de lo que eso significa, por lo que hacemos una macro que se convierte en 'extern' C "' en C++ y nada en C. La razón por la que necesitamos extern "C" es porque C++ descifra los nombres al compilar. C no lo hace, así que al usar 'extern' C "' dejamos que el compilador sepa que debe compilarse como "C way". – GManNickG