it-swarm.com.de

C++ und C kombinieren - wie funktioniert #ifdef __cplusplus?

Ich arbeite an einem Projekt, das viel C -Code enthält. Wir haben angefangen, in C++ zu schreiben, mit der Absicht, auch den alten Code zu konvertieren. Ich bin etwas verwirrt darüber, wie C und C++ interagieren. Ich verstehe, dass der C++ - Compiler den C -Code nicht mit den C -Codes mit extern "C" umgibt, aber ich bin nicht ganz sicher, wie ich das implementieren soll.

Am Anfang jeder C -Headerdatei (nach den Include-Guards) haben wir also

#ifdef __cplusplus
extern "C" {
#endif

und unten schreiben wir

#ifdef __cplusplus
}
#endif

Zwischen den beiden haben wir alle unsere Includes, Typedefs und Funktionsprototypen. Ich habe ein paar Fragen, um zu sehen, ob ich das richtig verstanden habe:

  1. Wenn ich eine C++ - Datei A.hh habe, die Eine C - Headerdatei B.h enthält, enthält Eine weitere C - Headerdatei C.h. Wie funktioniert das? Ich denke, dass , Wenn der Compiler in B.h einsteigt, __cplusplus definiert wird, so dass Den Code mit extern "C" Umgibt (und __cplusplus wird nicht in diesem Block definiert.). Wenn also ____ in C.h einsteigt, wird ______cplusplus nicht definiert Und der Code wird nicht in extern "C" eingeschlossen. Ist das richtig?

  2. Stimmt etwas nicht damit, dass Einen Code mit extern "C" { extern "C" { .. } } umgibt? Was wird der zweite extern "C"do?

  3. Wir legen diesen Wrapper nicht um die .c-Dateien, sondern nur um die .h-Dateien. Was passiert also, wenn eine Funktion keinen Prototyp hat? Denkt der Compiler, dass es sich um eine C++ - Funktion handelt?

  4. Wir verwenden auch einen -Code von Drittanbietern, der in C geschrieben ist und Diese Art von Wrapper nicht hatit. Jedes Mal, wenn ich einen Header Aus dieser Bibliothek einfüge, habe ich den #include .an einen extern "C" eingefügt. Ist dies der richtige Weg, um damit umzugehen?

  5. Ist dies schließlich eine gute Idee? Gibt es noch etwas, was wir tun sollten? Wir werden in absehbarer Zeit C und C++ __. möchten sicherstellen, dass wir alle unserer Basen abdecken.

274
dublev

extern "C" ändert nicht wirklich die Art und Weise, wie der Compiler den Code liest. Wenn sich Ihr Code in einer .c-Datei befindet, wird er als C kompiliert. Wenn er sich in einer .cpp-Datei befindet, wird er als C++ kompiliert (es sei denn, Sie tun Ihrer Konfiguration etwas Ungewöhnliches).

extern "C" wirkt sich auf die Verknüpfung aus. Wenn C++ - Funktionen kompiliert wurden, werden ihre Namen nicht mehr definiert. Dies macht Überladen möglich. Der Funktionsname wird je nach Typ und Anzahl der Parameter geändert, sodass zwei Funktionen mit demselben Namen unterschiedliche Symbolnamen haben.

Code in einem extern "C" ist immer noch C++ - Code. Es gibt Einschränkungen, was Sie in einem externen "C" -Block tun können, aber es geht nur um die Verknüpfung. Sie können keine neuen Symbole definieren, die nicht mit C-Verknüpfung erstellt werden können. Das bedeutet zum Beispiel keine Klassen oder Vorlagen.

extern "C"-Blöcke verschachteln sich gut. Es gibt auch extern "C++", wenn Sie hoffnungslos in extern "C"-Regionen gefangen sind, aber aus Sicht der Sauberkeit ist dies keine gute Idee.

Nun speziell zu Ihren nummerierten Fragen:

Zu # 1: __cplusplus sollte in extern "C"-Blöcken definiert werden. Dies spielt jedoch keine Rolle, da die Blöcke ordentlich nisten sollten.

Zu # 2: __cplusplus wird für jede Compilierungseinheit definiert, die durch den C++ - Compiler ausgeführt wird. Im Allgemeinen sind dies CPP-Dateien und alle Dateien, die in dieser CPP-Datei enthalten sind. Dasselbe .h (oder .hh oder .hpp oder Was haben Sie?) Kann zu unterschiedlichen Zeitpunkten als C oder C++ interpretiert werden, wenn unterschiedliche Kompilierungseinheiten dies beinhalten. Wenn die Prototypen in der .h-Datei auf C-Symbolnamen verweisen sollen, müssen sie extern "C" haben, wenn sie als C++ interpretiert werden, und sie sollten extern "C" nicht haben, wenn sie als C interpretiert werden - daher die #ifdef __cplusplus-Prüfung.

Um Ihre Frage Nr. 3 zu beantworten: Funktionen ohne Prototypen verfügen über eine C++ - Verknüpfung, wenn sie in .cpp-Dateien und nicht in einem extern "C"-Block enthalten sind. Dies ist jedoch in Ordnung, denn wenn es keinen Prototyp gibt, kann es nur von anderen Funktionen in derselben Datei aufgerufen werden, und es ist Ihnen im Allgemeinen egal, wie die Verknüpfung aussieht, da Sie nicht beabsichtigen, diese Funktion zu haben von irgendetwas außerhalb derselben Kompilierungseinheit aufgerufen werden.

Für # 4 haben Sie es genau verstanden. Wenn Sie einen Header für Code mit C-Verknüpfung angeben (z. B. Code, der von einem C-Compiler kompiliert wurde), müssen Sie extern "C" den Header eingeben. Auf diese Weise können Sie eine Verknüpfung mit der Bibliothek herstellen. (Andernfalls würde Ihr Linker nach Funktionen mit Namen wie _Z1hic suchen, wenn Sie nach void h(int, char) suchen.

5: Diese Art des Mischens ist ein häufiger Grund, extern "C" zu verwenden, und ich sehe nichts Falsches, wenn Sie es so machen - stellen Sie einfach sicher, dass Sie verstehen, was Sie tun.

239
  1. extern "C" ändert weder das Vorhandensein noch das Fehlen des __cplusplus-Makros. Es ändert sich lediglich die Verknüpfung und das Namensmangling der verpackten Deklarationen.

  2. Sie können extern "C"-Blöcke recht gut verschachteln.

  3. Wenn Sie Ihre .c-Dateien als C++ kompilieren, wird alles, was sich nicht in einem extern "C"-Block und ohne einen extern "C"-Prototyp befindet, als C++ - Funktion behandelt. Wenn Sie sie als C kompilieren, wird natürlich alles eine C-Funktion sein.

  4. Ja

  5. Sie können C und C++ auf diese Weise sicher mischen.

36

Ein paar gotchas, die Colloraries von Andrew Shelanskys hervorragender Antwort sind, und etwas mit nicht einverstanden zu sein, ändert nichts an der Art und Weise, wie der Compiler den Code liest.

Da Ihre Funktionsprototypen als C kompiliert sind, können Sie nicht die gleichen Funktionsnamen mit unterschiedlichen Parametern überladen. Dies ist eines der Hauptmerkmale der Namensveränderung des Compilers. Es wird als Verbindungsproblem beschrieben, aber das ist nicht ganz richtig - Sie erhalten Fehler sowohl vom Compiler als auch vom Linker.

Die Compiler-Fehler werden auftreten, wenn Sie versuchen, C++ - Funktionen der Prototypdeklaration zu verwenden, z. B. Überladen. 

Die Linker-Fehler werden später auftreten, da Ihre Funktion scheinbar nicht gefunden wird, wenn Sie not über den extern-Wrapper "C" um Deklarationen verfügen und der Header in einer Mischung aus C- und C++ - Quellcode enthalten ist .

Ein Grund, die Benutzer davon abzuhalten, die Einstellung compile C als C++ zu verwenden, ist, weil dies bedeutet, dass der Quellcode nicht mehr portierbar ist. Diese Einstellung ist eine Projekteinstellung. Wenn eine .c-Datei in einem anderen Projekt abgelegt wird, wird sie nicht als C++ kompiliert. Ich möchte lieber, dass sich die Leute die Zeit nehmen, Dateisuffixe in .cpp umzubenennen.

19
Andy Dent

Es geht um das ABI, damit sowohl C- als auch C++ - Anwendungen problemlos C-Schnittstellen verwenden können.

Da die C-Sprache sehr einfach ist, war die Codegenerierung für verschiedene Compiler wie GCC, Borland C\C++, MSVC usw. über viele Jahre stabil.

Während C++ immer beliebter wird, müssen der neuen C++ - Domäne viele Dinge hinzugefügt werden (zum Beispiel wurde die Cfront schließlich bei AT & T aufgegeben, weil C nicht alle Funktionen abdecken konnte, die es benötigt). Wie Vorlage Feature und Generierung von Code zur Kompilierungszeit aus der Vergangenheit haben die verschiedenen Compiler-Anbieter die eigentliche Implementierung von C++ - Compiler und Linker tatsächlich separat durchgeführt, die eigentlichen ABIs sind überhaupt nicht mit C++ kompatibel Programm auf verschiedenen Plattformen.

Möglicherweise möchten die Leute das eigentliche Programm weiterhin in C++ implementieren, aber die alte C-Schnittstelle und ABI wie gewohnt beibehalten. Die Header-Datei muss deklarieren extern "C" {}, dies teilt dem Compiler mit Erzeuge kompatibles/altes/einfaches/einfaches C ABI für die Schnittstellenfunktionen wenn der Compiler C Compiler ist, nicht C++ Compiler.

2
Bo Zhou