Warum kann ein Compiler nicht vermeiden, eine Header-Datei selbst zweimal zu importieren?

13

Neu in C ++! Also las ich das hier: http://www.learncpp.com/cpp-tutorial/110-a-first-look-at-the-preprocessor/

Kopfschutz

Da Header-Dateien andere Header-Dateien enthalten können, kann es vorkommen, dass eine Header-Datei mehrmals enthalten ist.

Um dies zu vermeiden, geben wir Präprozessor-Direktiven vor. Aber ich bin mir nicht sicher - warum kann der Compiler nur ... nicht importieren , die gleiche Sache zweimal?

Angesichts der Tatsache, dass Header-Guards optional sind (aber anscheinend eine gute Vorgehensweise sind), denke ich fast, dass es Szenarien gibt, in denen Sie etwas zweimal importieren möchten. Obwohl ich mir überhaupt kein solches Szenario vorstellen kann. Irgendwelche Ideen?

Omega
quelle
Auf dem MS-Compiler gibt es einen #pragma onceBefehl, der den Compiler anweist, diese Datei nur einmal einzuschließen.
CodesInChaos

Antworten:

27

Sie können, wie neue Sprachen zeigen, dies tun.

Vor all den Jahren (als der C-Compiler aus mehreren unabhängigen Phasen bestand) wurde jedoch eine Entwurfsentscheidung getroffen. Um die Kompatibilität zu gewährleisten, muss der Vorprozessor nun auf eine bestimmte Weise vorgehen, um sicherzustellen, dass alter Code wie erwartet kompiliert wird.

Da C ++ die Art und Weise erbt, wie es Header-Dateien von C verarbeitet, wurden die gleichen Techniken beibehalten. Wir unterstützen eine alte Designentscheidung. Eine Änderung der Funktionsweise ist jedoch zu riskant. Viele Codes können möglicherweise beschädigt werden. Jetzt müssen wir neuen Benutzern der Sprache beibringen, wie man Include-Guards verwendet.

Es gibt ein paar Tricks mit Header-Dateien, die Sie absichtlich mehrmals einfügen (dies bietet tatsächlich eine nützliche Funktion). Wenn wir das Paradigma jedoch von Grund auf neu entwerfen, können wir dies zur nicht standardmäßigen Methode machen, um Dateien einzuschließen.

Martin York
quelle
7

Anders wäre es nicht so aussagekräftig, da sie sich dafür entschieden haben, die Kompatibilität mit C beizubehalten und somit mit einem Präprozessor und nicht mit einem herkömmlichen Verpackungssystem fortzufahren.

Eine Sache, die mir einfällt, ist, dass ich ein Projekt hatte, das eine API war. Ich hatte zwei Header-Dateien x86lib.hund x86lib_internal.h. Da intern sehr umfangreich war, habe ich die "öffentlichen" Bits in x86lib.h getrennt, damit die Benutzer keine zusätzliche Zeit für das Kompilieren einplanen mussten.

Dies führte jedoch zu einem lustigen Problem mit Abhängigkeiten, sodass ich einen Ablauf hatte, der in x86lib_internal so ablief

  1. Festlegen der internen Präprozessordefinition
  2. Include x86lib.h (was bei der Definition von internal klug war, sich auf eine bestimmte Weise zu verhalten)
  3. Machen Sie ein paar Sachen und stellen Sie einige Dinge vor, die in x86lib.h verwendet werden
  4. Stellen Sie AFTER Präprozessor-Definition ein
  5. Schließen Sie x86lib.h erneut ein (dieses Mal ignorierte es alles außer einem getrennten AFTER-Teil, der von Elementen von x86lib_internal abhing

Ich würde nicht sagen, dass dies der beste Weg ist, aber er hat das erreicht, was ich wollte.

Earlz
quelle
0

Eine Schwierigkeit beim automatischen Ausschluss von Duplikat-Headern besteht darin, dass der C-Standard bezüglich der Bedeutung von Include-Dateinamen relativ leise ist. Angenommen, die zu kompilierende Hauptdatei enthält Direktiven #include "f1.h"und #include "f2.h", und die für diese Direktiven gefundenen Dateien enthalten beide Direktiven #include "f3.h". Wenn f1.hund f2.hsich in verschiedenen Verzeichnissen befinden, die jedoch durch die Suche nach Include-Pfaden gefunden wurden, ist unklar, welche #includeAnweisungen in diesen Dateien zum Laden derselben f3.hoder verschiedener Dateien vorgesehen sind.

Noch schlimmer wird es, wenn man die Möglichkeiten von Include-Dateien einschließlich relativer Pfade hinzufügt. In einigen Fällen, in denen Header-Dateien relative Pfade für verschachtelte Include-Anweisungen verwenden und Änderungen an bereitgestellten Header-Dateien vermieden werden sollen, kann es erforderlich sein, eine Header-Datei an mehreren Stellen in der Verzeichnisstruktur eines Projekts zu duplizieren. Obwohl mehrere physische Kopien dieser Headerdatei vorhanden sind, sollten sie semantisch als eine einzelne Datei betrachtet werden.

Wenn die #pragma onceDirektive zulässt, dass ein Bezeichner oncemit der Semantik folgt , dass der Compiler die Datei überspringen soll, wenn der Bezeichner mit einer von einer zuvor angetroffenen #pragma onceDirektive übereinstimmt , ist die Semantik eindeutig. Ein Compiler, der erkennen konnte, dass eine #includeDirektive dieselbe #pragma onceDatei mit denselben Tags wie eine frühere lädt, konnte durch Überspringen der Datei ein wenig Zeit sparen, ohne sie erneut zu öffnen, aber eine solche Erkennung wäre semantisch nicht wichtig, da die Datei übersprungen würde, ob oder nicht, der Dateiname wurde als Übereinstimmung erkannt. Es sind mir jedoch keine Compiler bekannt, die auf diese Weise arbeiten. Wenn Sie einen Compiler haben, prüfen Sie, ob eine Datei mit dem Muster übereinstimmt, #ifndef someIdentifier / #define someIdentifier / #endif [for that ifndef] / nothing followingund behandeln Sie so etwas als äquivalent zu obigem #pragma once someIdentifierifsomeIdentifier bleibt definiert, ist im wesentlichen so gut.

Superkatze
quelle