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

84

Ich weiß das.

Aufruf der C-Funktion aus C ++:

Wenn meine Anwendung in C ++ wäre und ich Funktionen aus einer in C geschriebenen Bibliothek aufrufen müsste, 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 nicht entstellen C_library_functionund der Linker würde den gleichen Namen in seinen * .lib-Eingabedateien finden und das Problem ist gelöst.

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. Das Mangeln von Namen in C ++ verursacht hier Probleme. Linker beschwert sich über die ungelösten Symbole. Nun, ich kann den C ++ - Compiler nicht für mein C-Projekt verwenden, da das viele andere Dinge kaputt macht. Was ist der Ausweg?

Ich benutze übrigens MSVC

Krallen
quelle
Wenn Sie die C ++ - Bibliothek steuern
Ciro Santilli 法轮功 冠状 病 六四 事件 29
2
Mögliches Duplikat von Elegant C ++ von C
aufrufen

Antworten:

94

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 als extern "C" deklariert ist und über eine reine C-API verfügt (z. B. ohne Klassen), die die C ++ - Bibliothek umschließt. Anschließend verwenden Sie die von Ihnen erstellte reine C-Wrapper-Bibliothek.

Ihre C-API kann optional einem objektorientierten Stil folgen, obwohl 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);
 }
Michael Aaron Safyan
quelle
1
Wie verknüpft man die endgültige ausführbare Datei? Verwenden Sie C oder C ++? (Als ich C verwendete, konnte der Linker die C ++ - Funktion nicht finden; als ich C ++ verwendete, konnte der Linker std :: cout nicht finden.)
Zack
1
@Zack Wenn Sie mit dem C ++ - Compiler / Linker eine statische Bibliothek erstellen, die transitiv seine Abhängigkeiten enthält (einschließlich der C ++ - Standardbibliothek, die möglicherweise implizit von Ihrem Linker verknüpft wird), sollten Sie in der Lage sein, diese statische Bibliothek mit a mit C-Code zu verknüpfen C Compiler / Linker.
Michael Aaron Safyan
Können Sie eine Vorlage von C ++ nach C exportieren?
MarcusJ
Ich habe ein "hybrides" Verhalten, da meine Bibliothek sowohl von C ++ als auch von C-Code aufgerufen werden muss. In der musste *.cpp fileich auch die Funktionen in #ifdef _cplusplus+ einschließen extern "C", um "undefinierte Verweise auf" Fehler zur Verbindungszeit zu vermeiden.
Coconop
39

Ich würde es folgendermaßen machen:

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

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

Den C ++ - Code der Klasse AAA - Pure C ++ ändere ich 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;
}

Kompilieren dieser Klasse wie regelmäßig für C ++ durchgeführt. Dieser Code "weiß nicht", dass er von C-Code verwendet wird. Verwenden des Befehls:

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

Jetzt auch in C ++ einen C-Connector erstellen. Definieren in den Dateien aaa_c_connector.h, aaa_c_connector.cpp . Dieser Connector definiert eine C-Funktion namens AAA_sayHi (cosnt char * name) , die eine Instanz von AAA verwendet und deren 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 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

Kompilieren Sie es 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:

main.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, und wenn ich die ausführbare Datei ./c_aaa ausführe, erhalte ich die erwartete Ausgabe:

Hi David
Hi James

BEARBEITEN:

Unter einigen Linux-Distributionen -laaaund -lstdc++möglicherweise auch für den letzten Kompilierungsbefehl erforderlich. Danke an @AlaaM. für die Aufmerksamkeit

Etwas etwas
quelle
1
Sie können auch den lib-Link-Pfad mitgcc usecpp.c -L. -laaa_c_connector -Wl,-rpath,. -o c_aaa
Metaphox
was ist usecpp.c?
Alaa M.
1
Die letzte Kompilierungszeile sollte sein : gcc main.c -L. -laaa_c_connector -laaa -lstdc++ -o c_aaa. Beachten Sie die -laaaund-lstdc+++
Alaa M.
@AlaaM. jemand meinen Code bearbeitet und geändert main.czu usecpp.c. Ich habe es gerade zurückgesetzt ... Über den anderen Kommentar sollte es auch so funktionieren wie es ist. Die zweite Zusammenstellung verwendet -laaaund es sollte wahrscheinlich genug sein (ich habe es vor mehr als 1,5 Jahren geschrieben, ich erinnere mich nicht sehr gut, was ich getan habe, aber ich glaube, ich habe jede Zeile vor dem Posten getestet)
SomethingSomething
1
Ok, habe es als Notiz am Ende des Beitrags hinzugefügt. Danke für ihre Aufmerksamkeit!
Etwas Etwas
6

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

Hündchen
quelle
5

Angenommen, die C ++ - API ist C-kompatibel (keine Klassen, Vorlagen usw.), können Sie sie extern "C" { ... }genau wie beim Umkehren einbinden.

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

Marcelo Cantos
quelle
Nicht ganz ... die C ++ - Bibliothek müsste neu kompiliert werden.
Michael Aaron Safyan
Ach nein. Die C ++ - API ist vollständig objektorientiert.
Krallen
2
@claws, siehe meinen Artikel zum Erstellen von C-Code im OOP-Stil. Erstellen Sie eine Wrapper-Bibliothek, die diesen Stil mit einer C-Schnittstelle, aber einer zugrunde liegenden C ++ - Implementierung verwendet. Dann Link zur C-Schnittstelle.
Michael Aaron Safyan
Vielleicht möchten Sie einen Blick auf Wrapper-Tools wie swig, pyboost, ... werfen, die gleichwertige Aufgaben ausführen (jedoch nicht in Richtung C ...)
xtofl
2

Exportieren Sie Ihre C ++ - Funktionen als externes "C" (auch bekannt als C-Symbole) oder verwenden Sie das .def-Dateiformat, um nicht dekorierte Exportsymbole für den C ++ - Linker zu definieren, wenn er die C ++ - Bibliothek erstellt. Dann sollte der C-Linker keine Probleme beim Lesen haben

Necrolis
quelle
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;
}
Richard Wicks
quelle
2
Bitte erwägen Sie, Ihren Antworten eine Erklärung hinzuzufügen.
HMD
Eine kurze Erklärung, wie das Obige die Frage beantwortet, macht die Antwort für andere nützlicher.
SOS
-3

Sie können der Funktionsdeklaration das externe Schlüsselwort "C" voranstellen, z

extern "C" int Mycppfunction ()

{

// Code geht hier

return 0;

}}

Weitere Beispiele finden Sie bei Google nach dem Schlüsselwort "extern". Sie müssen noch einige Dinge tun, aber es ist nicht schwierig, viele Beispiele von Google zu erhalten.

Abubakari UmarblacKrussain Uma
quelle
Drei Abstimmungen und keine einzige Erklärung. Wie immer großartig sein, SO Community.
Zimano