extern "Cpp"
-
Hallo Allerseits.
Wenn ich in einem C++ Quelltext auf Elemente zugreifen will, die als C Quelltext programmiert worden sind, dann muß ich deren Header im aufrufenden C++ Quelltext wie folgt mit einem extern umfassen:
extern "C" { #include "my_c.h" }
Was aber muß ich angeben, wenn ich in einem C Quelltext auf Elemente zugreifen will, die als C++ Quelltext programmiert worden sind?
extern "Cpp" { #include "my_cpp.h" }
funktioniert scheinbar nicht.
MinGW-4 sagt mir dann:
expected identifier or '(' before string constant
Vielen Dank im Voraus,
Eckard Klotz.
-
Gar nicht. Verwende PODs oder baue C-Wrapper fuer deine C++ Bibliothek.
-
Hallo Knivil.
Danke für die Antwort. Allerdings bin ich etwas schwer von Begriff und daher wäre es schön, wenn Du mir etwas auf die Sprünge helfen könntest:
1. Was verstehts Du unter POD?
2. Wenn ich Wrapper baue, dann muß ich Die Doch auch in C schreiben und habe dann da das Problem oder nicht?Mit freundlichen Grüßen,
Eckard Klotz.
-
Eckard Klotz schrieb:
1. Was verstehts Du unter POD?
Gib den Begriff mal bei Wikipedia ein und suche dir das am besten passende raus.
-
Eckard Klotz schrieb:
2. Wenn ich Wrapper baue, dann muß ich Die Doch auch in C schreiben und habe dann da das Problem oder nicht?
Sie werden in C++ geschrieben und mittels "extern C" deklariert und in eine eigene Bibliothek gepackt.
-
Hallo Dirk B.
Danke für den Tip. In Wikipedia finde ich:
Print-on-Demand ist ein seit Mitte der 1990er Jahre angewandtes Publikationsverfahren für Kleinstauflagen von Büchern und Druckschriften.
Da klingelt es bei mir aber immer noch nicht.
Mit freundlichen Grüßen,
Eckard Klotz
-
Hallo Knivil.
Wenn Du schreibst:
Sie werden in C++ geschrieben und mittels "extern C" deklariert und in eine eigene Bibliothek gepackt.
Meinst Du dann, daß ich meinen Wrapper als library "my_wrapper.a" vorcompilieren muß um dann dessen Header in der aufrufenden C-Datei als "extern C" zu declarieren, obwohl die Lib als C++ kompilert worden ist?
Mit freundlichen Grüßen,
Eckard Klotz.
-
vorcompilieren muß um dann dessen Header in der aufrufenden C-Datei als "extern C" zu declarieren, obwohl die Lib als C++ kompilert worden ist?
Nein, C++ muss auch die "extern C" Deklaration kennen, damit der C++-Compiler sich an die C-Richtlinien haelt, bspw. kein name mangling. Die C++-Headerdatei sieht wie folgt aus (gerne noch mit ifdefs fuer __cplusplus):
#ifndef MY_CPP_H #define MY_CPP_H extern "C" { void MyCFunc1(..); short MyCFunc2(..); } #endif
Ich habe nochmals darueber nachgedacht:
Wenn ich in einem C++ Quelltext auf Elemente zugreifen will, die als C Quelltext programmiert worden sind, dann muß ich deren Header im aufrufenden C++ Quelltext wie folgt mit einem extern umfassen:
extern "C" { #include "my_c.h" }
"extern C" zwingt den C++-Compiler C-Richtlinien zu beachten, bspw. kein name mangling.
-
Damit das nicht untergeht: POD.
Für die Verwendung in C reicht es allerdings nicht aus, wenn ein Typ POD ist -- PODs können beispielsweise Member-Funktionen haben, und so was mag ein C-Compiler nicht gern. std::aligned_storage<foo>::type ist POD, aber durch einen C-Compiler wird man das sicher nicht kriegen. Und so weiter.
Was knivil vermutlich meint, ist, dass du dein API, um es in C verwenden zu können, ebenfalls mit C-Mitteln anlegen musst. Das ist eigentlich ziemlich offensichtlich -- es ist zum Beispiel stumpf nicht möglich, einem C-Compiler beizubringen, was
namespace funky { void my_funky_function(std::string const &foo); void my_funky_function(std::wstring const &foo); }
bedeutet.
Dagegen hat ein C++-Compiler keine Probleme, C-Code zu verstehen, und deswegen ist C der kleine, gemeinsame Nenner, auf den sie sich einigen können. Dabei ist dem C-Compiler ziemlich egal, dass einige der Funktionen, die er benutzt, in C++ geschrieben wurden, solange er sich mit diesem Code nicht befassen muss und er die Signatur versteht, und der C++-Compiler hat mit C-Signaturen eh kein Problem. So weit, so gut. Es gibt nur einen Haken an der Sache: In C++ können Funktionen überladen werden. Und damit kriegen wir zur Linkzeit ein Problem.
In C ist die Lage ziemlich simpel: Eine Funktion hat einen Namen, eine zweite Funktion des selben Namens gibt es nicht. Ein C-Compiler spuckt daher für eine Funktion namens "foo" üblicherweise ein Symbol namens "foo" aus, und damit kann der Linker umgehen. In C++ dagegen ist es problemlos möglich, etwa
void foo(int); void foo(char const *);
zu schreiben -- und daher kann ein C++-Compiler für eine Funktion "foo" nicht einfach das Symbol "foo" ausspucken, denn dann könnte es mehrere Symbole namens "foo" geben, und der Linker könnte den Namen nicht mehr eindeutig auflösen. Daher betreibt ein C++-Compiler das, was knivil schon erwähnt hat: Name-Mangling. Der Name der Funktion und ihre Parameter werden zu einem eindeutigen Bezeichner verwurstet, der dann als Linkersymbol benutzt wird. Für das genannte Beispiel spuckt g++ beispielsweise die Smybole
_Z3fooi _Z3fooPKc
aus. Und damit ergibt sich ein Problem: Habe ich in C++-Code die Funktion void foo(int) und möchte die von C-Code aus benutzen, erzeugt der C++-Compiler ein Symbol "_Z3fooi", aber der C-Compiler weist den Linker an, nach einem Symbol "foo" zu suchen. Der findet das nicht und beschwert sich bei dir. Umgekehrt: Habe ich die Funktion in C-Code, erzeugt der C-Compiler ein Symbol "foo", und der C++-Compiler weist den Linker an, nach "_Z3fooi" zu suchen. Wieder wird der das nicht finden können.
Da man dem C-Compiler kein Name-Mangling beibringen kann -- der hat überhaupt keine Ahnung, dass es so was gibt -- muss man es dem C++-Compiler abgewöhnen. Daher schreibt man um die entsprechenden Funktionsdeklarationen extern "C" -- so weiß der C++-Compiler, dass der betreffende Maschinen-Code C-kompatibel erzeugt wurde bzw. erzeugt werden muss und verhält sich entsprechend -- erzeugt ein Symbol "foo" bzw. weist den Linker an, ein solches zu suchen. (Überladen werden darf die Funktion dann natürlich nicht mehr)
Also:
extern "C" void foo(int); // bzw. extern "C" { void bar(void); void baz(struct something *); // ... }
...womit nur noch ein Problem bleibt: Der C-Compiler hat keine Ahnung, was extern "C" bedeuten soll. Wozu auch; er erzeugt Maschinencode ja eh nach C-Manier. Also muss man es für ihn unsichtbar machen:
#ifdef __cplusplus extern "C" { #endif void bar(void); void baz(struct something*); #ifdef __cplusplus } #endif
Das Makro __cplusplus wird von C-Compilern nicht definiert, wohl aber von C++-Compilern. So schneidet der C-Compiler die für ihn uninteressanten Teile einfach raus, während der C++-Compiler sich mit ihnen befassen kann.
Und so hast du die Lösung des Problems: Baue deinen Header wie folgt auf:
#ifdef INCLUDE_GUARD_MUST_BE_UNIQUE_PER_HEADER_BEST_TO_INCLUDE_HEADER_NAME #ifdef INCLUDE_GUARD_MUST_BE_UNIQUE_PER_HEADER_BEST_TO_INCLUDE_HEADER_NAME #ifdef __cplusplus extern "C" { #endif // C-kompatible Struct- und Funktionsdeklarationen hier #ifdef __cplusplus } #endif #endif
...und binde diesen Header überall ein, wo du die Funktionen benutzt oder definierst, damit alle Bescheid wissen.
-
Eckard Klotz schrieb:
Hallo Dirk B.
Danke für den Tip. In Wikipedia finde ich:
Print-on-Demand ist ein seit Mitte der 1990er Jahre angewandtes Publikationsverfahren für Kleinstauflagen von Büchern und Druckschriften.
Da klingelt es bei mir aber immer noch nicht.
Mit freundlichen Grüßen,
Eckard KlotzBei mir erscheint u.a.
Wikipedia schrieb:
Plain Old Data structure, in der Programmiersprache C++ eine Bezeichnung für einfache Datentypen sowie Klassen und Strukturen
-
Hallo Allerseits.
Allen, die mir geholfen haben vielen Dank. Speziell Knivil und Seldon haben mir sehr weitergeholfen.
Dirk B sein eingestanden, daß ich inzwischen auch besser mit Wikipedia umgehen kann. Bei meinen bisherigen Versuchen habe ich mich nur auf die Vorschläge unter der Sucheingabe verlassen. Erst als ich diese ingnoriert habe bekamm die große Liste.
Also von allen habe ich was gelernt und dafür vielen Dank.
Mit freundlichen Grüßen,
Eckard Klotz.