106 votes

Comment appeler une fonction C++ à partir de C ?

Je le sais.

Appel d'une fonction C à partir de C++ :

Si mon application était en C++ et que je devais appeler des fonctions d'une bibliothèque écrite en C, j'aurais utilisé

//main.cpp

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

Cela ne dénaturerait pas le nom C_library_function et l'éditeur de liens trouverait le même nom dans ses fichiers *.lib en entrée et le problème serait résolu.

Appeler une fonction C++ à partir du C ???

Mais ici, je développe une grande application écrite en C et j'ai besoin d'utiliser une bibliothèque écrite en C++. La manipulation des noms en C++ pose problème ici. L'éditeur de liens se plaint des symboles non résolus. Je ne peux pas utiliser le compilateur C++ sur mon projet C parce que cela casse beaucoup d'autres choses. Quelle est la solution ?

Par ailleurs, j'utilise MSVC

124voto

Michael Aaron Safyan Points 45071

Vous devez créer une API C pour exposer les fonctionnalités de votre code C++. En fait, vous devrez écrire un code C++ déclaré extern "C" et disposant d'une API C pure (n'utilisant pas de classes, par exemple) qui englobe la bibliothèque C++. Vous utilisez ensuite la bibliothèque C pure que vous avez créée.

Votre API C peut éventuellement suivre un style orienté objet, même si le langage C n'est pas orienté objet. 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);
}

65voto

user3322273 Points 373

Je procéderais de la manière suivante :

(Si vous travaillez avec MSVC, ignorez les commandes de compilation de GCC)

Supposons que j'aie une classe C++ nommée AAA définis dans les fichiers aaa.h, aaa.cpp et que la classe AAA possède une méthode nommée sayHi(const char *nom) que je souhaite activer pour le code C.

Le code C++ de la classe AAA - C++ pur, je ne le modifie pas :

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;
}

Compiler cette classe comme on le fait régulièrement pour le C++. Ce code "ne sait pas" qu'il va être utilisé par du code C. En utilisant la commande :

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

Maintenant, toujours en C++, création d'un connecteur C :

Le définir dans les fichiers aaa_c_connector.h, aaa_c_connector.cpp . Ce connecteur va définir une fonction C, nommée AAA_sayHi(cosnt char *nom) qui utilisera une instance de AAA et appellera sa méthode :

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

Le compiler, encore une fois, en utilisant une commande de compilation C++ normale :

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

J'ai maintenant une bibliothèque partagée (libaaa_c_connector.so), qui implémente la fonction C AAA_sayHi(const char *nom) . Je peux maintenant créer un fichier principal en C et compiler le tout :

main.c

#include "aaa_c_connector.h"

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

    return 0;
}

Compilation à l'aide d'une commande de compilation C :

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

Je devrai définir LD_LIBRARY_PATH pour qu'il contienne $PWD, et si je lance l'exécutable ./c_aaa J'obtiendrai le résultat escompté :

Hi David
Hi James

EDIT :

Sur certaines distributions Linux, -laaa et -lstdc++ peut également être nécessaire pour la dernière commande de compilation. Merci à @AlaaM. pour son attention.

6voto

Marcelo Cantos Points 91211

En supposant que l'API C++ soit compatible avec le langage C (pas de classes, de modèles, etc.), vous pouvez l'envelopper dans un fichier extern "C" { ... } comme vous l'avez fait dans l'autre sens.

Si vous voulez exposer des objets et d'autres choses mignonnes en C++, vous devrez écrire une API enveloppante.

6voto

Puppy Points 90818

Pour ce faire, vous devrez écrire un wrapper pour le C en C++. Le C++ est rétrocompatible, mais le C n'est pas rétrocompatible.

2voto

Necrolis Points 17569

Exporter vos fonctions C++ en tant que extern "C" (symboles de style C), ou utiliser le format de fichier .def pour définir des symboles d'exportation non décorés pour l'éditeur de liens C++ lorsqu'il crée la bibliothèque C++, alors l'éditeur de liens C ne devrait pas avoir de problèmes pour la lire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X