¿Cómo llamar a la función C ++ desde C?


84

Yo se esto.

Llamar a la función C desde C ++:

Si mi aplicación estuviera en C ++ y tuviera que llamar a funciones desde una biblioteca escrita en C. Entonces habría usado

//main.cpp

extern "C" void C_library_function(int x, int y);//prototype
C_library_function(2,4);// directly using it.

Esto no alteraría el nombre C_library_functiony el enlazador encontraría el mismo nombre en sus archivos de entrada * .lib y el problema se resolvería.

Llamando a la función C ++ desde C ???

Pero aquí estoy extendiendo una gran aplicación que está escrita en C y necesito usar una biblioteca que está escrita en C ++. La alteración de nombres de C ++ está causando problemas aquí. Linker se queja de los símbolos no resueltos. Bueno, no puedo usar el compilador C ++ sobre mi proyecto C porque eso está rompiendo muchas otras cosas. ¿Dónde está la salida?

Por cierto, estoy usando MSVC




2
Posible duplicado de Elegantly Call C ++ desde C
user2284570

Respuestas:


95

Necesita crear una API C para exponer la funcionalidad de su código C ++. Básicamente, necesitará escribir código C ++ que se declare extern "C" y que tenga una API C pura (sin usar clases, por ejemplo) que envuelva la biblioteca C ++. Luego, usa la biblioteca contenedora de C pura que ha creado.

Su API C puede seguir opcionalmente un estilo orientado a objetos, aunque C no está orientado a objetos. Ex:

 // *.h file
 // ...
 #ifdef __cplusplus
 #define EXTERNC extern "C"
 #else
 #define EXTERNC
 #endif

 typedef void* mylibrary_mytype_t;

 EXTERNC mylibrary_mytype_t mylibrary_mytype_init();
 EXTERNC void mylibrary_mytype_destroy(mylibrary_mytype_t mytype);
 EXTERNC void mylibrary_mytype_doit(mylibrary_mytype_t self, int param);

 #undef EXTERNC
 // ...


 // *.cpp file
 mylibrary_mytype_t mylibrary_mytype_init() {
   return new MyType;
 }

 void mylibrary_mytype_destroy(mylibrary_mytype_t untyped_ptr) {
    MyType* typed_ptr = static_cast<MyType*>(untyped_ptr);
    delete typed_ptr;
 }

 void mylibrary_mytype_doit(mylibrary_mytype_t untyped_self, int param) {
    MyType* typed_self = static_cast<MyType*>(untyped_self);
    typed_self->doIt(param);
 }


1
¿Cómo se vincula el ejecutable final? ¿Usas C o C ++? (Cuando usé C, el vinculador no pudo encontrar la función C ++; cuando usé C ++, el vinculador no pudo encontrar std :: cout.)
Zack

1
@Zack si crea una biblioteca estática con el compilador / enlazador de C ++ que incluye transitivamente sus dependencias (incluida la biblioteca estándar de C ++, que puede estar enlazada implícitamente por su enlazador), debería poder enlazar esa biblioteca estática con el código C utilizando un Compilador / enlazador de C.
Michael Aaron Safyan

¿Puede exportar una plantilla de C ++ a C?
MarcusJ

Tengo un comportamiento "híbrido" ya que necesito que mi biblioteca sea llamada tanto por código C ++ como por código C. En el *.cpp file, también necesitaba ajustar las funciones en #ifdef _cplusplus+ extern "C"para evitar errores de "referencia indefinida a" en el momento del enlace.
Coconop

41

Lo haría de la siguiente manera:

(Si trabaja con MSVC, ignore los comandos de compilación de GCC)

Supongamos que tengo una clase C ++ llamada AAA , definida en los archivos aaa.h, aaa.cpp , y que la clase AAA tiene un método llamado sayHi (const char * name) , que quiero habilitar para el código C.

El código C ++ de la clase AAA - Pure C ++, no lo modifico:

aaa.h

#ifndef AAA_H
#define AAA_H

class AAA {
    public:
        AAA();
        void sayHi(const char *name);
};

#endif

aaa.cpp

#include <iostream>

#include "aaa.h"

AAA::AAA() {
}

void AAA::sayHi(const char *name) {
    std::cout << "Hi " << name << std::endl;
}

Compilar esta clase como se hace habitualmente para C ++. Este código "no sabe" que va a ser utilizado por el código C. Usando el comando:

g++ -fpic -shared aaa.cpp -o libaaa.so

Ahora, también en C ++, creando un conector C. Definiéndolo en archivos aaa_c_connector.h, aaa_c_connector.cpp . Este conector va a definir una función C, llamada AAA_sayHi (cosnt char * name) , que usará una instancia de AAA y llamará a su método:

aaa_c_connector.h

#ifndef AAA_C_CONNECTOR_H 
#define AAA_C_CONNECTOR_H 

#ifdef __cplusplus
extern "C" {
#endif

void AAA_sayHi(const char *name);

#ifdef __cplusplus
}
#endif


#endif

aaa_c_connector.cpp

#include <cstdlib>

#include "aaa_c_connector.h"
#include "aaa.h"

#ifdef __cplusplus
extern "C" {
#endif

// Inside this "extern C" block, I can implement functions in C++, which will externally 
//   appear as C functions (which means that the function IDs will be their names, unlike
//   the regular C++ behavior, which allows defining multiple functions with the same name
//   (overloading) and hence uses function signature hashing to enforce unique IDs),


static AAA *AAA_instance = NULL;

void lazyAAA() {
    if (AAA_instance == NULL) {
        AAA_instance = new AAA();
    }
}

void AAA_sayHi(const char *name) {
    lazyAAA();
    AAA_instance->sayHi(name);
}

#ifdef __cplusplus
}
#endif

Compilarlo, nuevamente, usando un comando de compilación normal de C ++:

g++ -fpic -shared aaa_c_connector.cpp -L. -laaa -o libaaa_c_connector.so

Ahora tengo una biblioteca compartida (libaaa_c_connector.so), que implementa la función C AAA_sayHi (const char * name) . Ahora puedo crear un archivo principal en C y compilarlo todo junto:

main.c

#include "aaa_c_connector.h"

int main() {
    AAA_sayHi("David");
    AAA_sayHi("James");

    return 0;
}

Compilarlo usando un comando de compilación de C:

gcc main.c -L. -laaa_c_connector -o c_aaa

Necesitaré configurar LD_LIBRARY_PATH para que contenga $ PWD, y si ejecuto el ejecutable ./c_aaa , obtendré el resultado que espero:

Hi David
Hi James

EDITAR:

En algunas distribuciones de Linux, -laaay -lstdc++también puede ser necesario para el último comando de compilación. Gracias a @AlaaM. para la atención


1
también puede especificar la ruta del enlace lib congcc usecpp.c -L. -laaa_c_connector -Wl,-rpath,. -o c_aaa
Metaphox

lo que es usecpp.c?
Alaa M.

1
La última línea de compilación debe ser: gcc main.c -L. -laaa_c_connector -laaa -lstdc++ -o c_aaa. Tenga en cuenta el -laaay-lstdc+++
Alaa M.

@AlaaM. alguien editó mi código y cambió main.ca usecpp.c. Lo acabo de revertir ... Sobre el otro comentario, debería funcionar también como está. La segunda compilación usa -laaay probablemente debería ser suficiente (la escribí hace más de 1,5 años, no recuerdo muy bien lo que hice, pero creo que probé cada línea antes de publicar)
SomethingSomething

1
Ok, lo agregué como una nota al final de la publicación. ¡Gracias por la atención!
SomethingSomething

6

Tendrá que escribir un contenedor para C en C ++ si desea hacer esto. C ++ es compatible con versiones anteriores, pero C no es compatible con versiones anteriores.


5

Suponiendo que la API de C ++ es compatible con C (sin clases, plantillas, etc.), puede ajustarla extern "C" { ... } , tal como lo hizo cuando iba en sentido contrario.

Si desea exponer objetos y otras cosas interesantes de C ++, tendrá que escribir una API contenedora.


No del todo ... la biblioteca de C ++ necesitaría ser recompilada.
Michael Aaron Safyan

Oh no. La API de C ++ está totalmente orientada a objetos.
garras

2
@claws, vea mi artículo sobre cómo hacer código C estilo OOP ... y cree una biblioteca contenedora usando ese estilo con una interfaz C, pero una implementación C ++ subyacente. Luego, enlace a la interfaz C.
Michael Aaron Safyan

Es posible que desee echar un vistazo a las 'herramientas' de envoltura como swig, pyboost, ... que hacen cosas equivalentes (pero no hacia C ...)
xtofl

2

exporta tus funciones de C ++ como "C" externo (también conocido como símbolos de estilo C), o usa el formato de archivo .def para definir símbolos de exportación no decorados para el enlazador C ++ cuando crea la biblioteca C ++, entonces el enlazador C no debería tener problemas para leerlo


0
#include <iostream>

//////////////
// C++ code //
//////////////
struct A
{
  int i;
  int j;

  A() {i=1; j=2; std::cout << "class A created\n";}
  void dump() {std::cout << "class A dumped: " << i << ":" << j << std::endl;}
  ~A() {std::cout << "class A destroyed\n";}
};

extern "C" {
  // this is the C code interface to the class A
  static void *createA (void)
  {
    // create a handle to the A class
    return (void *)(new A);
  }
  static void dumpA (void *thisPtr)
  {
    // call A->dump ()
    if (thisPtr != NULL) // I'm an anal retentive programmer
    {
      A *classPtr = static_cast<A *>(thisPtr);
      classPtr->dump ();
    }
  }
  static void *deleteA (void *thisPtr)
  {
    // destroy the A class
    if (thisPtr != NULL)
    {
      delete (static_cast<A *>(thisPtr));
    }
  }
}

////////////////////////////////////
// this can be compiled as C code //
////////////////////////////////////
int main (int argc, char **argv)
{
  void *handle = createA();

  dumpA (handle);
  deleteA (handle);

  return 0;
}

2
Considere agregar alguna explicación a sus respuestas.
HMD

Una breve explicación de cómo lo anterior responde a la pregunta hace que la respuesta sea más útil para los demás.
SOS

-3

Puede prefijar la declaración de función con la palabra clave externa "C", por ejemplo

extern "C" int Mycppfunction ()

{

// El código va aquí

return 0;

}

Para obtener más ejemplos, puede buscar más en Google sobre la palabra clave "externa". Necesita hacer algunas cosas más, pero no es difícil que obtenga muchos ejemplos de Google.


Tres votos en contra y ni una sola explicación. Siendo genial como siempre, SO comunidad.
Zimano
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.