web-dev-qa-db-fra.com

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

Je sais ça.

Appel de la fonction C depuis C++:

Si mon application était en C++ et que je devais appeler des fonctions depuis une bibliothèque écrite en C. J'aurais alors utilisé 

//main.cpp

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

Cela ne modifierait 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.

Appel de la fonction C++ à partir de C ???

Mais ici, je suis en train d’étendre une grande application qui est écrite en C et je dois utiliser une bibliothèque qui est écrite en C++. Les mutilations de noms de C++ causent des problèmes ici. Linker se plaint des symboles non résolus. Bien, je ne peux pas utiliser le compilateur C++ sur mon projet C parce que casser beaucoup d’autres choses. Quel est le moyen de sortir?

En passant, j'utilise MSVC

62
claws

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

Votre API C peut éventuellement suivre un style orienté objet, même si 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);
 }
69

Je le ferais de la manière suivante:

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

Supposons que j'ai une classe C++ nomméeAAA, définie dans les fichiers aaa.h, aaa.cpp , et que la classeAAAa une méthode nommée sayHi ( const char * name) , que je veux activer pour le code C.

Le code C++ de la classeAAA- 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 cela se fait régulièrement pour C++. Ce code "ne sait pas" qu'il va être utilisé par le code C. En utilisant la commande:

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

Maintenant, également en C++, créer 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, appelée AAA_sayHi (cosnt char * name) , qui utilisera une instance deAAAet 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 define C functions that are able to call C++ code

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

En le compilant, encore une fois, en utilisant une commande de compilation C++ standard:

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

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

// principal c

#include "aaa_c_connector.h"

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

    return 0;
}

La compiler en utilisant une commande de compilation C:

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

J'aurai besoin de configurer LD_LIBRARY_PATH pour qu'il contienne $ PWD. Si j'exécute l'exécutable ./c_aaa , j'obtiendra le résultat attendu:

Hi David
Hi James

MODIFIER:

Sur certaines distributions Linux, -laaa et -lstdc++ peuvent également être requis pour la dernière commande de compilation. Merci à @AlaaM. pour l'attention

22
SomethingSomething

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

Si vous souhaitez exposer des objets et d'autres éléments C++ mignons, vous devez écrire une API wrapper.

5
Marcelo Cantos

Si vous voulez faire cela, vous devrez écrire un wrapper pour C en C++. C++ est compatible avec les versions antérieures, mais C n'est pas compatible avec les versions antérieures.

5
Puppy

exportez vos fonctions C++ en tant que extern "C" (ou symboles de style C) ou utilisez le format de fichier .def pour définir des symboles d'exportation non décorés pour le lieur C++ lorsqu'il crée la bibliothèque C++;

2
Necrolis
#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;
}
0
Richard Wicks