Mam funkcję C, którą chciałbym wywołać z C ++. Nie mogłem użyć extern "C" void foo()
podejścia typu „ ”, ponieważ funkcja C nie została skompilowana przy użyciu g ++. Ale kompiluje się dobrze przy użyciu gcc. Jakieś pomysły, jak wywołać funkcję z C ++?
90
g++
komunikaty o błędachvoid valid_in_C_but_not_in_CPlusPlus(size_t size) { char variable_length_array[size]; }
void f(void *pv) { int *pi = pv; *pi = 42; }
^^Odpowiedzi:
Skompiluj kod C w następujący sposób:
gcc -c -o somecode.o somecode.c
Następnie kod C ++ taki jak ten:
g++ -c -o othercode.o othercode.cpp
Następnie połącz je ze sobą za pomocą konsolidatora C ++:
g++ -o yourprogram somecode.o othercode.o
Musisz także powiedzieć kompilatorowi C ++, że nagłówek C nadchodzi, gdy dołączasz deklarację funkcji C. Więc
othercode.cpp
zaczyna się od:extern "C" { #include "somecode.h" }
somecode.h
powinien zawierać coś takiego:#ifndef SOMECODE_H_ #define SOMECODE_H_ void foo(); #endif
(W tym przykładzie użyłem gcc, ale zasada jest taka sama dla każdego kompilatora. Skompiluj osobno odpowiednio jako C i C ++, a następnie połącz je razem.)
źródło
extern "C"
w nagłówku znak#ifdef __cplusplus
.Pozwólcie mi zebrać fragmenty z innych odpowiedzi i komentarzy, aby dać przykład z czysto oddzielonymi kodami C i C ++:
Część C:
foo.h :
#ifndef FOO_H #define FOO_H void foo(void); #endif
foo.c
#include "foo.h" void foo(void) { /* ... */ }
Skompiluj to z
gcc -c -o foo.o foo.c
.Część C ++:
bar.cpp
extern "C" { #include "foo.h" //a C header, so wrap it in extern "C" } void bar() { foo(); }
Skompiluj to z
g++ -c -o bar.o bar.cpp
A potem połącz to wszystko razem:
g++ -o myfoobar foo.o bar.o
Uzasadnienie: kod C powinien być zwykłym kodem w języku C, bez
#ifdef
„może kiedyś zadzwonię z innego języka”. Jeśli jakiś programista C ++ wywołuje twoje funkcje C, to ich problem, jak to zrobić, a nie twój. A jeśli jesteś programistą C ++, to nagłówek C może nie być twój i nie powinieneś go zmieniać, więc obsługa niezarządzanych nazw funkcji (tj.extern "C"
) Należy do twojego kodu C ++.Możesz oczywiście napisać sobie wygodny nagłówek C ++, który nie robi nic poza opakowaniem nagłówka C w
extern "C"
deklarację.źródło
Zgadzam się z odpowiedzią prof. Falkena , ale po komentarzu Arne Mertza chcę podać pełny przykład (najważniejsza część to
#ifdef __cplusplus
):somecode.h
#ifndef H_SOMECODE #define H_SOMECODE #ifdef __cplusplus extern "C" { #endif void foo(void); #ifdef __cplusplus } #endif #endif /* H_SOMECODE */
somecode.c
#include "somecode.h" void foo(void) { /* ... */ }
othercode.hpp
#ifndef HPP_OTHERCODE #define HPP_OTHERCODE void bar(); #endif /* HPP_OTHERCODE */
othercode.cpp
#include "othercode.hpp" #include "somecode.h" void bar() { foo(); // call C function // ... }
Następnie postępuj zgodnie z instrukcjami prof. Falkena, aby skompilować i połączyć.
Działa to, ponieważ podczas kompilacji z
gcc
makro__cplusplus
nie jest zdefiniowane, więc nagłóweksomecode.h
zawarty w programiesomecode.c
wygląda tak po przetworzeniu wstępnym:void foo(void);
a podczas kompilacji z
g++
, to__cplusplus
jest zdefiniowane, więc nagłówek zawarty wothercode.cpp
jest teraz taki:extern "C" { void foo(void); }
źródło
#ifdef __cplusplus
kod w C. Kod C jest niższym poziomem i nie powinno się martwić, jeśli pewnego dnia zostanie wywołany z kodu C ++. Imo, które#ifdef
ma swoje zastosowanie tylko w kodzie C ++, jeśli chcesz zapewnić nagłówek powiązania C dla biblioteki napisanej w C ++, a nie odwrotnie.Ta odpowiedź jest inspirowana przypadkiem, w którym uzasadnienie Arne'a było poprawne. Dostawca napisał bibliotekę, która kiedyś obsługiwała zarówno C, jak i C ++; Jednak najnowsza wersja obsługiwała tylko C. Następujące śladowe dyrektywy pozostawione w kodzie wprowadzały w błąd:
#ifdef __cplusplus extern "C" { #endif
Kosztowało mnie to kilka godzin przy próbie kompilacji w C ++. Samo wywołanie C z C ++ było znacznie łatwiejsze.
Konwencja ifdef __cplusplus narusza zasadę pojedynczej odpowiedzialności. Kod korzystający z tej konwencji próbuje jednocześnie wykonać dwie rzeczy:
To tak, jakby próbować pisać jednocześnie po angielsku amerykańskim i brytyjskim. Jest to niepotrzebne wrzucenie #ifdef __thequeensenglish spanner #elif __yankeeenglish wrench #else bezużytecznego narzędzia, które utrudnia odczytanie kodu #endif w kodzie.
W przypadku prostego kodu i małych bibliotek może działać konwencja ifdef __cplusplus; jednakże w przypadku złożonych bibliotek najlepiej jest wybrać jeden lub drugi język i trzymać się go. Obsługa jednego z języków wymaga mniej konserwacji niż próba obsługi obu.
To jest zapis modyfikacji, które wprowadziłem w kodzie Arne, aby skompilować go w systemie Ubuntu Linux.
foo.h :
#ifndef FOO_H #define FOO_H void foo(void); #endif
foo.c
#include "foo.h" #include <stdio.h> void foo(void) { // modified to verify the code was called printf("This Hello World was called in C++ and written in C\n"); }
bar.cpp
extern "C" { #include "foo.h" //a C header, so wrap it in extern "C" } int main() { foo(); return(0); }
Makefile
# -*- MakeFile -*- # dont forget to use tabs, not spaces for indents # to use simple copy this file in the same directory and type 'make' myfoobar: bar.o foo.o g++ -o myfoobar foo.o bar.o bar.o: bar.cpp g++ -c -o bar.o bar.cpp foo.o: foo.c gcc -c -o foo.o foo.c
źródło