Come chiamare la funzione C ++ da C?


84

Lo so.

Chiamare la funzione C da C ++:

Se la mia applicazione fosse in C ++ e avessi dovuto chiamare funzioni da una libreria scritta in C. Allora avrei usato

//main.cpp

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

Questo non altererebbe il nome C_library_functione il linker troverà lo stesso nome nei file * .lib di input e il problema sarà risolto.

Chiamare la funzione C ++ da C ???

Ma qui sto estendendo una grande applicazione scritta in C e ho bisogno di usare una libreria scritta in C ++. La modifica del nome di C ++ sta causando problemi qui. Linker si lamenta dei simboli irrisolti. Beh, non posso usare il compilatore C ++ sul mio progetto C perché questo sta rompendo molte altre cose. Qual è l'uscita?

A proposito, sto usando MSVC




2
Possibile duplicato di Elegantly call C ++ da C
user2284570

Risposte:


95

È necessario creare un'API C per esporre la funzionalità del codice C ++. Fondamentalmente, dovrai scrivere codice C ++ che è dichiarato extern "C" e che ha un'API C pura (che non utilizza classi, ad esempio) che avvolge la libreria C ++. Quindi usi la libreria wrapper C pura che hai creato.

La tua API C può opzionalmente seguire uno stile orientato agli oggetti, anche se C non è orientato agli oggetti. 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);
 }


1
Come si collega l'eseguibile finale? Usi C o C ++? (Quando ho usato C, il linker non è riuscito a trovare la funzione C ++; quando ho usato C ++, il linker non ha trovato std :: cout.)
Zack

1
@Zack se crei una libreria statica con il compilatore / linker C ++ che include transitivamente le sue dipendenze (inclusa la libreria standard C ++, che può essere implicitamente collegata dal tuo linker), dovresti essere in grado di collegare quella libreria statica al codice C utilizzando un Compilatore / linker C.
Michael Aaron Safyan

Puoi esportare un modello da C ++ a C?
MarcusJ

Ho un comportamento "ibrido" in quanto ho bisogno che la mia libreria venga chiamata sia da codice C ++ che da codice C. In *.cpp fileho anche bisogno di avvolgere le funzioni in #ifdef _cplusplus+ extern "C"per evitare errori di "riferimento indefinito" al momento del collegamento.
Coconop

43

Lo farei nel modo seguente:

(Se lavori con MSVC, ignora i comandi di compilazione GCC)

Supponiamo che io abbia una classe C ++ chiamata AAA , definita nei file aaa.h, aaa.cpp , e che la classe AAA abbia un metodo chiamato sayHi (const char * name) , che voglio abilitare per il codice C.

Il codice C ++ della classe AAA - Pure C ++, non lo modifico:

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

Compilando questa classe come si fa regolarmente per C ++. Questo codice "non sa" che verrà utilizzato dal codice C. Utilizzando il comando:

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

Ora, anche in C ++, creando un connettore C. Definendolo nei file aaa_c_connector.h, aaa_c_connector.cpp . Questo connettore definirà una funzione C, denominata AAA_sayHi (cosnt char * name) , che utilizzerà un'istanza di AAA e chiamerà il suo metodo:

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

Compilarlo, di nuovo, usando un normale comando di compilazione C ++:

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

Ora ho una libreria condivisa (libaaa_c_connector.so), che implementa la funzione C AAA_sayHi (const char * name) . Ora posso creare un file principale C e compilarlo tutto insieme:

main.c

#include "aaa_c_connector.h"

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

    return 0;
}

Compilarlo utilizzando un comando di compilazione C:

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

Dovrò impostare LD_LIBRARY_PATH per contenere $ PWD e se eseguo l'eseguibile ./c_aaa , otterrò l'output che mi aspetto:

Hi David
Hi James

MODIFICARE:

Su alcune distribuzioni Linux, -laaae -lstdc++potrebbe anche essere richiesto per l'ultimo comando di compilazione. Grazie a @AlaaM. per l'attenzione


1
puoi anche specificare il percorso del collegamento lib congcc usecpp.c -L. -laaa_c_connector -Wl,-rpath,. -o c_aaa
Metaphox

cosa usecpp.c?
Alaa M.

1
L'ultima riga di compilazione dovrebbe essere: gcc main.c -L. -laaa_c_connector -laaa -lstdc++ -o c_aaa. Nota la -laaae-lstdc+++
Alaa M.

@AlaaM. qualcuno ha modificato il mio codice e l'ha cambiato main.cin usecpp.c. L'ho appena ripristinato .... Riguardo l'altro commento, dovrebbe funzionare anche così com'è. La seconda compilation usa -laaae probabilmente dovrebbe essere sufficiente (l'ho scritta più di 1,5 anni fa, non ricordo molto bene cosa ho fatto, ma penso di aver testato ogni riga prima di postare)
SomethingSomething

1
Ok, l'ho aggiunto come nota alla fine del post. Grazie per l'attenzione!
SomethingSomething

6

Dovrai scrivere un wrapper per C in C ++ se vuoi farlo. C ++ è compatibile con le versioni precedenti, ma non è compatibile con le versioni precedenti.


5

Supponendo che l'API C ++ sia compatibile con C (senza classi, modelli, ecc.), Puoi avvolgerlo extern "C" { ... }, proprio come hai fatto quando sei andato dall'altra parte.

Se vuoi esporre oggetti e altre cose carine in C ++, dovrai scrivere un'API wrapper.


Non proprio ... la libreria C ++ dovrebbe essere ricompilata.
Michael Aaron Safyan

Oh no. L'API C ++ è completamente orientata agli oggetti.
artigli

2
@claws, vedi il mio articolo sulla creazione di codice C in stile OOP .. e crea una libreria wrapper usando quello stile con un'interfaccia C, ma un'implementazione C ++ sottostante. Quindi collegarsi all'interfaccia C.
Michael Aaron Safyan

Potresti voler dare un'occhiata agli 'strumenti' wrapper come swig, pyboost, ... che fanno cose equivalenti (ma non verso C ...)
xtofl

2

esporta le tue funzioni C ++ come "C" esterno (ovvero simboli di stile C), o usa il formato di file .def per definire simboli di esportazione non decorati per il linker C ++ quando crea la libreria C ++, quindi il linker C non dovrebbe avere problemi a leggerlo


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

2
Per favore considera di aggiungere qualche spiegazione alle tue risposte.
HMD

Una breve spiegazione di come quanto sopra risponde alla domanda rende la risposta più utile per gli altri.
SOS

-3

È possibile anteporre alla dichiarazione di funzione la parola chiave "C" esterna, ad es

extern "C" int Mycppfunction ()

{

// Il codice va qui

return 0;

}

Per ulteriori esempi puoi cercare di più su Google sulla parola chiave "extern". Devi fare poche cose in più, ma non è difficile ottenere molti esempi da Google.


Tre voti negativi e non una sola spiegazione. Essere grandi come sempre, così community.
Zimano
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.