wake-up-neo.com

Wie rufe ich die C++ - Funktion von C auf?

Ich weiß das.

Aufruf der C-Funktion aus C++:

Wenn meine Anwendung in C++ war und ich Funktionen aus einer in C geschriebenen Bibliothek aufrufen musste, dann hätte ich verwendet 

//main.cpp

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

Dies würde den Namen C_library_function nicht beeinträchtigen, und der Linker würde denselben Namen in seinen Eingabe-.lib-Dateien finden und das Problem wurde behoben.

Aufruf der C++ - Funktion von C ???

Aber hier erweitere ich eine große Anwendung, die in C geschrieben ist, und ich muss eine Bibliothek verwenden, die in C++ geschrieben ist. Namensmangel von C++ macht hier Probleme. Linker beschwert sich über die ungelösten Symbole. Nun, ich kann keinen C++ - Compiler für mein C-Projekt verwenden, da sonst vieles zerstört wird. Was ist der Ausweg?

Übrigens verwende ich MSVC

62
claws

Sie müssen eine C-API erstellen, um die Funktionalität Ihres C++ - Codes verfügbar zu machen. Grundsätzlich müssen Sie C++ - Code schreiben, der für extern "C" deklariert ist und über eine reine C-API verfügt (die beispielsweise keine Klassen verwendet), die die C++ - Bibliothek einschließt. Dann verwenden Sie die reine C-Wrapper-Bibliothek, die Sie erstellt haben.

Ihre C-API kann optional einem objektorientierten Stil folgen, auch wenn C nicht objektorientiert ist. 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

Ich würde es auf folgende Weise tun:

(Wenn Sie mit MSVC arbeiten, ignorieren Sie die GCC-Kompilierungsbefehle.)

Angenommen, ich habe eine C++ - Klasse namensAAA, definiert in Dateien aaa.h, aaa.cpp , und die KlasseAAAhat eine Methode namens sayHi ( const char * name) , das ich für C-Code aktivieren möchte.

Der C++ - Code der KlasseAAA- Reines C++, ich modifiziere es nicht:

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

Diese Klasse wird wie für C++ regelmäßig kompiliert. Dieser Code "weiß nicht", dass er von C-Code verwendet wird. Verwenden Sie den Befehl:

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

Nun auch in C++ einen C-Connector erstellen. Definiert in files aaa_c_connector.h, aaa_c_connector.cpp . Dieser Connector definiert eine C-Funktion mit dem Namen AAA_sayHi (cosnt char * name) , die eine Instanz vonAAAverwendet und ihre Methode aufruft:

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

Kompilieren erneut mit einem regulären C++ - Kompilierungsbefehl:

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

Jetzt habe ich eine gemeinsam genutzte Bibliothek (libaaa_c_connector.so), die die C-Funktion AAA_sayHi (const char * name) implementiert. Ich kann jetzt eine C-Hauptdatei erstellen und alles zusammen kompilieren:

// Haupt c

#include "aaa_c_connector.h"

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

    return 0;
}

Kompilieren mit einem C-Kompilierungsbefehl:

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

Ich muss LD_LIBRARY_PATH so einstellen, dass es $ PWD enthält. Wenn ich die ausführbare Datei ./c_aaa ausführte, erhalte ich die erwartete Ausgabe:

Hi David
Hi James

BEARBEITEN:

In einigen Linux-Distributionen können auch -laaa und -lstdc++ für den letzten Kompilierungsbefehl erforderlich sein. Danke an @AlaaM. für die Aufmerksamkeit

22

Unter der Annahme, dass die C++ - API C-kompatibel ist (keine Klassen, Vorlagen usw.), können Sie sie in extern "C" { ... } einschließen, genau wie Sie es getan haben.

Wenn Sie Objekte und andere hübsche C++ - Elemente verfügbar machen möchten, müssen Sie eine Wrapper-API schreiben.

5
Marcelo Cantos

Wenn Sie dies tun möchten, müssen Sie in C++ einen Wrapper für C schreiben. C++ ist abwärtskompatibel, aber C ist nicht abwärtskompatibel.

5
Puppy

exportieren Sie Ihre C++ - Funktionen als externes "C" (auch als C-Symbol bezeichnet), oder definieren Sie undekorierte Exportsymbole für den C++ - Linker beim Erstellen der C++ - Bibliothek mit dem Dateiformat .def. Dann sollte der C-Linker beim Lesen keine Probleme haben

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