Jak wywołać funkcję C++ z C?

Wiem o tym.

Wywołanie funkcji C Z C++:

Gdyby moja aplikacja była w C++ i musiałbym wywoływać funkcje z biblioteki napisanej w C. Wtedy użyłbym

//main.cpp

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

To nie zmieniłoby nazwy C_library_function i linker znalazłby tę samą nazwę w swoim wejściu *.pliki lib i problem rozwiązany.

Wywołanie funkcji C++ Z C???

Ale tutaj rozszerzam dużą aplikację, która jest napisana w C i muszę użyć biblioteki, która jest napisany w C++. Wymachiwanie nazw C++ powoduje tutaj kłopoty. Linker narzeka na nierozwiązane symbole. Cóż, nie mogę używać kompilatora C++ nad moim projektem C, ponieważ to łamie wiele innych rzeczy. Jakie jest wyjście?

Przy okazji używam MSVC

Author: Jonathan Leffler, 2010-04-30

6 answers

Musisz utworzyć C API, aby ujawnić funkcjonalność kodu C++. Zasadniczo, będziesz musiał napisać kod C++, który jest zadeklarowany extern " C " i który ma czyste C API (Nie używając klas, na przykład), który zawija bibliotekę C++. Następnie używasz stworzonej biblioteki Pure C wrapper.

Twój API C może opcjonalnie podążać za stylem obiektowym, nawet jeśli C nie jest obiektowym. 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);
 }
 66
Author: Michael Aaron Safyan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-09 09:51:54

Zrobiłbym to w następujący sposób:

(jeśli pracujesz z MSVC, ignoruj polecenia kompilacji GCC)

Załóżmy, że mam klasę C++ o nazwie AAA , zdefiniowaną w plikach aaa.h, aaa.cpp, oraz że Klasa AAA ma metodę o nazwie sayHi (const char * name) , którą chcę włączyć dla kodu C.

Kod C++ klasy AAA - czysty C++, nie modyfikuję go:

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

Kompilowanie tej klasy, tak jak to robi się regularnie dla C++. Kod ten "nie wie" ĹĽe bÄ ™ dzie uĺľywany przez kod C. Użycie polecenia:

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

Teraz, również w C++, tworzenie złącza C. Definiowanie go w plikach aaa_c_connector.h, aaa_c_connector.cpp . Ten łącznik zdefiniuje funkcję C o nazwie AAA_sayHi (cosnt char *name) , która użyje instancji AAA i wywoła jej metodę:

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

Skompilowanie go, ponownie, za pomocą zwykłego polecenia kompilacji C++:

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

Teraz mam wspólną bibliotekę (libaaa_c_connector.so), która implementuje funkcję C AAA_sayHi(const char *name) . Mogę teraz utworzyć główny plik C i skompilować go razem:

// main.c

#include "aaa_c_connector.h"

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

    return 0;
}

Kompilowanie za pomocą polecenia kompilacji C:

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

Będę musiał ustawić LD_LIBRARY_PATH zawierać $PWD, a jeśli uruchamiam plik wykonywalny ./c_aaa , uzyskam wyjście, którego oczekuję:

Hi David
Hi James

EDIT:

W niektórych dystrybucjach Linuksa, -laaa i -lstdc++ mogą być również wymagane do wykonania ostatniego polecenia kompilacji. Dzięki @AlaaM. dla uwagi

 19
Author: SomethingSomething,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-30 13:54:45

Zakładając, że API C++ jest zgodne z C (Brak klas, szablonów itp.), można go zapakować extern "C" { ... }, tak jak robiono to w drugą stronę.

Jeśli chcesz eksponować obiekty i inne słodkie rzeczy C++, musisz napisać wrapper API.

 5
Author: Marcelo Cantos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-30 11:41:10

Będziesz musiał napisać wrapper dla C w C++, jeśli chcesz to zrobić. C++ jest kompatybilny wstecz, ale C nie jest kompatybilny do przodu.

 5
Author: Puppy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-30 11:43:31

Export your C++ functions as extern " C " (aka c style symbols), or use the .format pliku def do zdefiniowania niedekorowanych symboli Eksportu dla linkera C++ gdy tworzy bibliotekę C++, wtedy linker C nie powinien mieć problemów z odczytaniem go

 2
Author: Necrolis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-30 11:40:10
#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
Author: Richard Wicks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-13 01:10:33