2011-09-02 9 views
44

Desarrollamos algún proyecto en plano C (C99). Pero, tenemos una biblioteca como códigos fuente (biblioteca matemática) en C++. Necesitamos esta biblioteca, por lo que me gustaría preguntar: ¿cuál es la forma más elegante de integrar estos códigos fuente?Elegantemente llame a C++ desde C

Relación entre los tamaños de C y C++ es 20:1 por lo que pasar a C++ no es la opción. ¿Deberíamos usar una biblioteca estática? ¿DLL? (Todo está en Windows).

+8

¿La biblioteca C++ proporciona una interfaz C adecuada (funciones gratuitas en lugar de funciones miembro, sin excepciones, extern "C", etc.)? –

+0

Preguntas frecuentes sobre la mezcla de C y C++: http://www.parashift.com/c++-faq/mixing-c-and-cpp.html – Maciej

+1

posible duplicación de [¿Cómo llamar a la función C++ desde C?] (Http: // stackoverflow.com/questions/2744181/how-to-call-c-function-from-c) –

Respuesta

50

EDIT: Sobre la base de la discusión en el comentario, debo señalar que la separación de las cosas en una compatibles C-struct duck y una derivada class Duck es probablemente innecesario. Probablemente pueda descargar la implementación de forma segura en struct duck y eliminar class Duck, obviando real(…). Pero no conozco suficientemente C++ (en particular, la forma en que interactúa con el universo C) para ofrecer una respuesta definitiva sobre esto.


No hay ninguna razón por la que no pueda simplemente vincular todos sus códigos C y C++ en un solo binario.

La conexión con el código C++ requiere que ajuste la API de C++ en una API de C. Puede hacerlo declarando un conjunto de funciones dentro de extern "C" { ... } al compilar el código C++ y sin la declaración extern al compilar el código del cliente C. Por ejemplo:

#ifdef __cplusplus 
extern "C" { 
#endif 

typedef struct duck duck; 

duck* new_duck(int feet); 
void delete_duck(duck* d); 
void duck_quack(duck* d, float volume); 

#ifdef __cplusplus 
} 
#endif 

Se puede definir la estructura del pato en su C fuente de C++, e incluso heredar la verdadera clase Duck de ella:

struct duck { }; 

class Duck : public duck { 
public: 
    Duck(int feet); 
    ~Duck(); 

    void quack(float volume); 
}; 

inline Duck* real(duck* d) { return static_cast<Duck*>(d); } 

duck* new_duck(int feet) { return new Duck(feet); } 
void delete_duck(duck* d) { delete real(d); } 
void duck_quack(duck* d, float volume) { real(d)->quack(volume); } 
+0

¡Gran respuesta, gracias! – Cartesius00

+0

Los punteros vacíos de la IMO serían más apropiados aquí – Ulterior

+32

@Ulterior: No IMO. Si representas a los patos y las ranas como punteros vacíos, puedes accidentalmente obtener una rana para cuajar, lo que desgarraría la estructura del universo. –

7

La única razón para querer heredar de la estructura de pato sería para exponer algunos a sus atributos en la API C, que generalmente se considera mal estilo de todos modos. Sin herencia, su cabecera C podría tener este aspecto:

struct Duck; 

struct Duck* new_Duck(int feet); 
void delete_Duck(struct Duck* d); 
void Duck_quack(struct Duck* d, float volume); 

Y esta sería la ejecución correspondiente, sin necesidad de conversiones de tipo:

extern "C" { 
#include "Duck.h" 
} 

class Duck { 
public: 
    Duck(int feet) : {} 
    ~Duck() {} 

    void quack(float volume) {} 
}; 

struct Duck* new_Duck(int feet) { return new Duck(feet); } 
void delete_Duck(struct Duck* d) { delete d; } 
void Duck_quack(struct Duck* d, float volume) { d->quack(volume); } 

De la misma manera, una API de C puede ser creado para una interfaz C++ (clase virtual pura) y sus implementaciones. En ese caso, solo el constructor debe basarse en la implementación concreta (por ejemplo, new_RubberDuck (2)). El destructor y todas las otras funciones operarán automáticamente en la implementación correcta, al igual que en C++.

+2

Las implementaciones de funciones también deben etiquetarse como extern "C". De lo contrario, obtienes errores de enlace porque "new_Duck :: i" o cualquier nombre que se genere en tu plataforma hace que las funciones de C++ no coincidan con el "nuevo_Pads" del encabezado. – uliwitness

5

Una biblioteca matemática en C++ puede ser implementada en las clases de utilidad (solo miembros estáticos). En este caso, un enfoque mucho más simple podría ser tomado:

class FPMath { 
public: 
    static double add(double, double); 
    static double sub(double, double); 
    static double mul(double, double); 
    static double div(double, double); 
}; 

La cabecera de la interfaz de C sería entonces:

double FPMath_add(double, double); 
double FPMath_sub(double, double); 
double FPMath_mul(double, double); 
double FPMath_div(double, double); 

Y la aplicación correspondiente podría ser:

double FPMath_add(double a, double b) { return FPMath::add(a, b); } 
double FPMath_sub(double a, double b) { return FPMath::sub(a, b); } 
double FPMath_mul(double a, double b) { return FPMath::mul(a, b); } 
double FPMath_div(double a, double b) { return FPMath::div(a, b); } 

Pero tal vez esto indica lo obvio ...

0

Hay es una forma de crear un "truco" que le permite llamar directamente a funciones miembro de algunos objetos.

Lo primero que debe hacer es crear una función de fábrica extern "C", que devuelve un puntero (como void*) al objeto.

Lo segundo que necesita es el nombre mutilado de la función de miembro.

A continuación, puede llamar a la función utilizando el nombre destruido y pasar el puntero devuelto de la función de fábrica como primer argumento.

Advertencias:

  • , por supuesto, no funciona llamando a la función miembro que quiera otros objetos, o referencias, u otras cosas C++, o funciones que devuelven objetos o tipos que no sean compatibles con los tipos C
  • no funcionará en funciones miembro virtuales, y probablemente no en objetos con funciones virtuales en ellos, incluso si no es una función virtual que se llama
  • El nombre revuelto tiene por qué ser un símbolo válido C
  • Cualquier muchos muchos más ...

Esto no es algo que recomiendo, sino todo lo contrario. Recomiendo encarecidamente no hacer algo como se describe en esta respuesta. Es un comportamiento no compatible y probablemente indefinido y puede romperse de maneras extrañas e impredecibles.