2010-02-04 69 views
14

Estoy intentando acceder a una clase C++ y llamar a su método desde un archivo .c.Cómo llamar a una clase C++ y su método desde un archivo c

I Google este tema y parece que este http://developers.sun.com/solaris/articles/mixing.html

Dice:

You can write extern "C" functions in C++ that access class M objects and call them from C code.

Aquí es una función de C++ diseñada para llamar a la función miembro foo:

extern "C" int call_M_foo(M* m, int i) { return m->foo(i); } 

Mi pregunta ¿Dónde coloco la línea de alrededor? En mi archivo C++ .h? O C .h archivo?

Y sigue y dice:

Aquí es un ejemplo de código C que utiliza la clase M:

struct M;      // you can supply only an incomplete declaration 

int call_M_foo(struct M*, int); // declare the wrapper function 

int f(struct M* p, int j)  // now you can call M::foo 
{ 
    return call_M_foo(p, j); 
} 

Pero, ¿cómo/dónde puedo crear la clase M en mi archivo C ? ¿Y dónde pongo el código anterior? C .h archivo? C++ .h archivo? O C .c archivo?

Gracias.

Gracias por GMan 's respuesta detallada. Seguí tu sugerencia. Pero obtengo un error de compilación en mi archivo .c.

main.c:33:
./some_class.h:24: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘attribute’ before ‘’ token
./some_class.h:25: error: expected ‘)’ before ‘
’ token
./some_class.h:26: error: expected ‘)’ before ‘*’ token

Y aquí están mis some_class.h línea 24-26:

#ifdef __cplusplus 
class M { 

public: 
    M(); 
    virtual ~M(); 

    void method1(char* name, char* msg); 
}; 

extern "C" { 
#else 
struct M; 
#endif 

    /* access functions line 24-26 are here*/ 
    M* M_new(void); 
    void M_delete(M*); 
    void M_method1(M*, char*, char*); 
#ifdef __cplusplus 
} 
#endif 

Por alguna razón, mi compilador de C no le gusta extern "C" en GMan 's originales some_test.h. Entonces tengo que modificarlo a arriba. Parece que el compilador de C no le gusta/comprende la línea struct M;.

Cualquier idea será muy apreciada.

+0

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/ –

+0

@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

Respuesta

25

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".

+0

Una liendre: las funciones some_class_construct/destruct deben llamarse some_class_new/delete, ya que eso es lo que hacen. Llamarlos construir/destruir es muy confuso –

+0

@Chris: Bueno, ellos * hacen * construir y destruir, pero tienes razón; ese no es su objetivo principal. – GManNickG

+0

@GMan - en C, si solo hace 'struct name', no puede referirse a él simplemente como' name'. Para hacer eso, debes hacer 'typedef struct nombre nombre' –

3

Debe dividirlo entre el encabezado de C++ y los archivos de implementación.

foo.h:

extern "C" int call_M_foo(M* m, int i); 

foo.cc:

extern "C" int call_M_foo(M* m, int i) { 
    return m->foo(i); 
} 

Para crear el objeto de tipo M, se necesitaría una función similar:

foo.h:

struct M; 
extern "C" M* create_M(); 

foo.CC:

extern "C" M* create_M() { 
    return new M; 
} 
0

El sitio que ha vinculado a tiene ya la respuesta:

You can declare function print in a header file that is shared by C and C++ code:

#ifdef __cplusplus extern "C" 
#endif int print(int i, double d); 

You can declare at most one function of an overloaded set as extern "C" Here is the example C header for the wrapper functions:

int g_int(int); 
double g_double(double); 

Básicamente, no puede haber una cabecera compartida entre los dos que declara el prototipo de función, agregando el modificador externo "C" si está en C++ para asegurar que se pueda acceder a la función en un objeto de C. Usted define el cuerpo de la función n más adelante en el código de C++ como de costumbre, si es necesario dentro de una clase, etc., y usa la función en C como normal.

2

Tiene varias preguntas combinadas aquí, así que las responderé individualmente.

My question is where do I put the about line? In my c++ .h file? or c .h file?

La línea extern "C" va en el archivo C++.Básicamente le dice al compilador limitar todo dentro del bloque extern "C" al subconjunto C de C++, y a las funciones de exportación declaradas en esta área en consecuencia.

But how/where do I create the class M in my c file?

No se puede. C no tiene el concepto de clases, y no hay absolutamente ninguna forma de instanciar una clase directamente. En esencia, tiene que exportar una función C en su archivo C++ que crea la clase y la devuelve como un puntero. Entonces puede pasar ese puntero alrededor de su aplicación C. En realidad, no puede modificar la clase directamente en su aplicación C, porque C no admite clases y su compilador C++ puede insertar variables "ocultas" para la contabilidad dentro de la declaración real de la clase.

And where do I put the above code?

El trozo de código que utiliza un puntero struct Ure va en el archivo C. Usted es obligado a utilizar un puntero ure struct porque C no admite clases en absoluto. Puede poner llamadas a funciones usando esa función en cualquier parte de un archivo de implementación C , al igual que las llamadas a funciones C normales.

1

Toda la información que necesita está en el enlace que proporciona. Solo necesita entender que debe haber una separación estricta entre los códigos C y C++.

  1. código C++ puede llamada de cualquier código C.
  2. código C generalmente no se puede llamar a cualquier código C++.
  3. Las funciones C se pueden implementar mediante código C++.

La parte clave para entender es que ++ la C y C compiladores mangle nombres de función al crear archivos de objetos de diferentes maneras, por lo que normalmente no serían capaces de interoperar (en tiempo de enlace), excepto que C++ puede ser incitado a saber la diferencia mediante el uso de extern "C"

el prototipo: void f(int); podría ser destrozado por un compilador C para: _f, sino un compilador de C++ puede elegir un nombre muy diferente, por ejemplo f_int, por lo que el enlazador no sabría que se supone ser el mismo

Sin embargo:

extern "C" void f(int);

sería mutilado por un compilador de C++ para _f, pero un compilador C se ahogara en el extern "C". Para evitar esto, usted debe usado algo como esto:

#ifdef __cplusplus 
extern "C" { 
#endif 

void f(int); 

#ifdef __cplusplus 
} /* closing brace for extern "C" */ 
#endif 

Ahora el conjunto de la sección anterior se puede vivir en un archivo .h y es, como los estados sun.com artículo, una cabecera de lenguaje mixto.

Esto significa que un archivo .co .cpp puede #include esta cabecera y el código puede llamar f();
y ya sea una o .c.cpp puede #include esta cabecera y ponerlo en práctica:

void f() 
{ 
} 

Ahora bien poco es que un archivo .cpp puede aplicar esto a llamar a cualquier código C++ que le gusta.

Ahora para responder a sus preguntas específicas:

  1. El primer ejemplo de código sólo puede ir en un archivo .cpp.
  2. El segundo ejemplo de código solo puede ir en un archivo .c.

Además, la clase M debe declararse y definirse solo en archivos C++.

Cuestiones relacionadas