Wiem to.
Wywołanie funkcji C z C ++:
Gdyby moja aplikacja była w C ++ i musiałbym wywołać funkcje z biblioteki napisanej w C. Wtedy bym użył
//main.cpp
extern "C" void C_library_function(int x, int y);//prototype
C_library_function(2,4);// directly using it.
Nie zmieniłoby to nazwy, C_library_function
a konsolidator znalazłby tę samą nazwę w swoich wejściowych plikach * .lib i problem został rozwiązany.
Wywołanie funkcji C ++ z C ???
Ale tutaj rozszerzam dużą aplikację napisaną w C i potrzebuję biblioteki napisanej w C ++. Zniekształcanie nazw w C ++ powoduje tutaj problemy. Linker narzeka na nierozwiązane symbole. Cóż, nie mogę użyć kompilatora C ++ w moim projekcie C, ponieważ to psuje wiele innych rzeczy. Jakie jest wyjście?
Przy okazji używam MSVC
c++
c
visual-c++
extern-c
pazury
źródło
źródło
Odpowiedzi:
Aby udostępnić funkcjonalność kodu w języku C ++, musisz utworzyć interfejs API języka C. Zasadniczo będziesz musiał napisać kod w C ++, który jest zadeklarowany jako extern "C" i który ma czysty interfejs API języka C (na przykład nie używający klas), który otacza bibliotekę C ++. Następnie używasz czystej biblioteki C wrapper, którą stworzyłeś.
Twój interfejs API języka C może opcjonalnie być zgodny ze stylem zorientowanym obiektowo, nawet jeśli język C nie jest zorientowany obiektowo. Dawny:
// *.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); }
źródło
*.cpp file
musiałem również zawinąć funkcje w#ifdef _cplusplus
znak +,extern "C"
aby uniknąć błędów „niezdefiniowanego odniesienia do” w czasie łączenia.Zrobiłbym to w następujący sposób:
(Jeśli pracujesz z MSVC, zignoruj polecenia kompilacji GCC)
Załóżmy, że mam klasę C ++ o nazwie AAA , zdefiniowaną w plikach aaa.h, aaa.cpp i ż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 w sposób regularny dla C ++. Ten kod „nie wie”, że będzie używany przez kod C. Za pomocą polecenia:
g++ -fpic -shared aaa.cpp -o libaaa.so
Teraz, także w C ++, tworzenie łącznika 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 będzie używać instancji AAA i wywoła swoją 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 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
Kompiluję go ponownie, używając zwykłego polecenia kompilacji C ++:
g++ -fpic -shared aaa_c_connector.cpp -L. -laaa -o libaaa_c_connector.so
Teraz mam udostępnioną bibliotekę (libaaa_c_connector.so), która implementuje funkcję C AAA_sayHi (const char * name) . Mogę teraz utworzyć główny plik C i skompilować wszystko razem:
main.c
#include "aaa_c_connector.h" int main() { AAA_sayHi("David"); AAA_sayHi("James"); return 0; }
Kompilowanie go za pomocą polecenia kompilacji w C:
gcc main.c -L. -laaa_c_connector -o c_aaa
Będę musiał ustawić LD_LIBRARY_PATH tak, aby zawierał $ PWD, a jeśli uruchomię plik wykonywalny ./c_aaa , otrzymam oczekiwany wynik:
EDYTOWAĆ:
W niektórych dystrybucjach Linuksa
-laaa
i-lstdc++
może być również wymagane dla ostatniego polecenia kompilacji. Dzięki @AlaaM. dla uwagiźródło
gcc usecpp.c -L. -laaa_c_connector -Wl,-rpath,. -o c_aaa
usecpp.c
?gcc main.c -L. -laaa_c_connector -laaa -lstdc++ -o c_aaa
. Uwaga-laaa
i-lstdc+++
main.c
nausecpp.c
. Właśnie go cofnąłem ... Jeśli chodzi o drugi komentarz, powinien również działać tak, jak jest. Druga kompilacja używa-laaa
i chyba powinno wystarczyć (napisałem ją ponad 1,5 roku temu, nie pamiętam dobrze, co zrobiłem, ale chyba każdą linijkę przetestowałem przed wysłaniem)Będziesz musiał napisać opakowanie dla C w C ++, jeśli chcesz to zrobić. C ++ jest kompatybilny wstecz, ale C nie jest kompatybilny z poprzednimi wersjami.
źródło
Zakładając, że API C ++ jest kompatybilne z C (bez klas, szablonów itp.), Możesz je opakować
extern "C" { ... }
, tak jak robiłeś to w drugą stronę.Jeśli chcesz udostępnić obiekty i inne urocze rzeczy w C ++, będziesz musiał napisać API wrappera.
źródło
wyeksportuj swoje funkcje C ++ jako zewnętrzne „C” (aka symbole w stylu C) lub użyj formatu pliku .def do zdefiniowania niezabezpieczonych symboli eksportu dla konsolidatora C ++ podczas tworzenia biblioteki C ++, wówczas konsolidator C nie powinien mieć problemów z odczytaniem tego
źródło
#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; }
źródło
Deklarację funkcji można poprzedzić słowem kluczowym extern „C”, np
extern „C” int Mycppfunction ()
{
// Kod jest tutaj
return 0;
}
Więcej przykładów można znaleźć w Google na temat słowa kluczowego „extern”. Musisz zrobić jeszcze kilka rzeczy, ale nie jest to trudne, otrzymasz wiele przykładów z Google.
źródło