Exemplarische Vorgehensweise: Erstellen und Importieren von Headereinheiten in Microsoft Visual C++

In diesem Artikel geht es um das Erstellen und Importieren von Headereinheiten mit Visual Studio 2022. Informationen zum Importieren von C++-Standardbibliotheksheadern als Kopfzeileneinheiten finden Sie unter Exemplarische Vorgehensweise: Importieren von STL-Bibliotheken als Kopfzeileneinheiten. Eine noch schnellere und robustere Methode zum Importieren der Standardbibliothek finden Sie im Lernprogramm: Importieren der C++-Standardbibliothek mithilfe von Modulen.

Headereinheiten sind die empfohlene Alternative zu vorkompilierten Headerdateien (Precompiled Header, PCH). Kopfzeileneinheiten sind einfacher einzurichten und zu verwenden, sind deutlich kleiner auf dem Datenträger, bieten ähnliche Leistungsvorteile und sind flexibler als ein gemeinsam genutzter PCH.

Wenn Sie Kopfzeileneinheiten mit anderen Möglichkeiten zum Einschließen von Funktionen in Ihre Programme kontrastieren möchten, lesen Sie " Vergleichen von Kopfzeileneinheiten, Modulen und vorkompilierten Headern".

Voraussetzungen

Um Headereinheiten verwenden zu können, benötigen Sie Visual Studio 2019 Version 16.10 oder höher.

Was ist eine Headereinheit?

Eine Headereinheit ist eine binäre Darstellung einer Headerdatei. Eine Headereinheit endet auf eine .ifc-Erweiterung. Das gleiche Format wird für benannte Module verwendet.

Ein wichtiger Unterschied zwischen einer Kopfzeileneinheit und einer Headerdatei besteht darin, dass eine Kopfzeileneinheit nicht von Makrodefinitionen außerhalb der Kopfzeileneinheit beeinflusst wird. Das heißt, Sie können kein Präprozessorsymbol definieren, das bewirkt, dass sich die Kopfzeileneinheit anders verhält. Wenn Sie die Kopfzeileneinheit importieren, wird die Kopfzeileneinheit bereits kompiliert. Das unterscheidet sich von der Behandlung einer #include Datei. Eine eingeschlossene Datei kann von einer Makrodefinition außerhalb der Headerdatei beeinflusst werden, da die Headerdatei den Präprozessor durchläuft, wenn Sie die Quelldatei kompilieren, die sie enthält.

Kopfzeileneinheiten können in beliebiger Reihenfolge importiert werden, was nicht auf Headerdateien zutrifft. Die Reihenfolge der Headerdatei ist wichtig, da makrodefinitionen, die in einer Headerdatei definiert sind, sich auf eine nachfolgende Headerdatei auswirken können. Makrodefinitionen in einer Kopfzeileneinheit können sich nicht auf eine andere Kopfzeileneinheit auswirken.

Alles, was aus einer Headerdatei sichtbar ist, ist auch aus einer Kopfzeileneinheit sichtbar, einschließlich Makros, die innerhalb der Kopfzeileneinheit definiert sind.

Eine Headerdatei muss in eine Kopfzeileneinheit übersetzt werden, bevor sie importiert werden kann. Ein Vorteil von Headereinheiten gegenüber vorkompilierten Headerdateien (PCH) besteht darin, dass sie in verteilten Builds verwendet werden können. Solange Sie das .ifc Und das Programm kompilieren, das es mit demselben Compiler importiert und auf dieselbe Plattform und Architektur ausgerichtet ist, kann eine auf einem Computer erstellte Kopfzeileneinheit auf einem anderen Computer genutzt werden. Im Gegensatz zu einem PCH, wenn sich eine Kopfzeile ändert, wird nur sie neu erstellt und hängt davon ab. Kopfzeileneinheiten können bis zu einer Größenordnung kleiner sein als ein .pch.

Headereinheiten legen weniger Einschränkungen für die erforderlichen Ähnlichkeiten von Compilerschalterkombinationen fest, die zum Erstellen der Kopfzeileneinheit und zum Kompilieren des Codes verwendet werden, der sie verwendet, als ein PCH. Einige Switchkombinationen und Makrodefinitionen können jedoch Verstöße gegen die eine Definitionsregel (ODR) zwischen verschiedenen Übersetzungseinheiten erzeugen.

Schließlich sind Kopfzeileneinheiten flexibler als ein PCH. Bei einem PCH können Sie sich nicht dafür entscheiden, nur einen der Header in der PCH-Datei einzugeben– der Compiler verarbeitet alle. Wenn Sie Kopfzeileneinheiten zusammen in eine statische Bibliothek kompilieren, bringen Sie nur den Inhalt der Kopfzeileneinheit, die Sie in Ihre Anwendung importieren.

Kopfzeileneinheiten sind ein Schritt zwischen Headerdateien und C++20-Modulen. Sie bieten einige der Vorteile von Modulen. Sie sind robuster, da sich externe Makrodefinitionen nicht auf sie auswirken – sodass Sie sie in beliebiger Reihenfolge importieren können. Und der Compiler kann sie schneller verarbeiten als Headerdateien. Kopfzeileneinheiten haben jedoch nicht alle Vorteile von Modulen, da Kopfzeileneinheiten die darin definierten Makros verfügbar machen (Module nicht). Im Gegensatz zu Modulen gibt es keine Möglichkeit, private Implementierungen in einer Kopfzeileneinheit auszublenden. Um die private Implementierung mit Headerdateien anzugeben, werden verschiedene Techniken verwendet, z. B. das Hinzufügen von führenden Unterstrichen zu Namen oder das Einfügen von Elementen in einen Implementierungsnamespace. Ein Modul macht keine private Implementierung in irgendeiner Form verfügbar, daher müssen Sie dies nicht tun.

Erwägen Sie, Ihre vorkompilierten Kopfzeilen durch Kopfzeileneinheiten zu ersetzen. Sie erhalten den gleichen Geschwindigkeitsvorteil, aber auch mit anderen Codehygiene- und Flexibilitätsvorteilen.

Möglichkeiten zum Kompilieren einer Headereinheit

Es gibt verschiedene Möglichkeiten, eine Datei in eine Headereinheit zu kompilieren:

  • Erstellen eines Projekts mit freigegebenen Headereinheiten: Wir empfehlen diesen Ansatz, da sie mehr Kontrolle über die Organisation und Wiederverwendung der importierten Kopfzeileneinheiten bietet. Erstellen Sie ein statisches Bibliotheksprojekt, das die gewünschten Kopfzeileneinheiten enthält, und verweisen Sie dann auf das Projekt, um die Kopfzeileneinheiten zu importieren. Eine exemplarische Vorgehensweise für diesen Ansatz finden Sie unter Erstellen eines statischen Bibliotheksprojekts für Kopfzeileneinheiten.

  • Wählen Sie einzelne Dateien aus, die in Kopfzeileneinheiten übersetzt werden sollen. Mit diesem Ansatz können Sie dateiweise steuern, was als Kopfzeileneinheit behandelt wird. Es ist auch hilfreich, wenn Sie eine Datei als Kopfzeileneinheit kompilieren müssen, da sie nicht über die Standarderweiterung (.ixx, , .cppm, .h, .hpp) verfügt, normalerweise nicht in eine Kopfzeileneinheit kompiliert wird. Dieser Ansatz wird in dieser exemplarischen Vorgehensweise gezeigt. Informationen zu den ersten Schritten finden Sie unter "Ansatz 1: Übersetzen einer bestimmten Datei in eine Kopfzeileneinheit".

  • Sucht automatisch nach Kopfzeileneinheiten und erstellt sie. Dieser Ansatz ist praktisch, eignet sich aber am besten für kleinere Projekte, da es keinen optimalen Builddurchsatz garantiert. Ausführliche Informationen zu diesem Ansatz finden Sie unter Ansatz 2: Automatische Suche nach Kopfzeileneinheiten.

  • Wie in der Einführung Erwähnung, können Sie STL-Headerdateien als Kopfzeileneinheiten erstellen und importieren und automatisch für STL-Bibliotheksheader so behandeln#include, als ob import Sie Ihren Code neu schreiben müssen. Informationen dazu finden Sie unter Walkthrough: Importieren von STL-Bibliotheken als Kopfzeileneinheiten.

Ansatz 1: Übersetzen einer bestimmten Datei in eine Kopfzeileneinheit

In diesem Abschnitt wird gezeigt, wie Sie eine bestimmte Datei auswählen, die in eine Kopfzeileneinheit übersetzt werden soll. Kompilieren einer Headerdatei als Kopfzeileneinheit mithilfe der folgenden Schritte in Visual Studio:

  1. Erstellen Sie ein neues C++-Konsolen-App-Projekt.

  2. Ersetzen Sie den Inhalt der Quelldatei durch den folgenden Code:

    #include "Pythagorean.h"
    
    int main()
    {
        PrintPythagoreanTriple(2,3);
        return 0;
    }
    
  3. Fügen Sie eine Headerdatei hinzu, die aufgerufen wird Pythagorean.h , und ersetzen Sie dann den Inhalt durch diesen Code:

    #ifndef PYTHAGOREAN
    #define PYTHAGOREAN
    
    #include <iostream>
    
    inline void PrintPythagoreanTriple(int a, int b)
    {
        std::cout << "Pythagorean triple a:" << a << " b:" << b << " c:" << a*a + b*b << std::endl;
    }
    #endif
    

Festlegen von Projekteigenschaften

Um Headereinheiten zu aktivieren, legen Sie zuerst den C++-Sprachstandard mit den folgenden Schritten auf /std:c++20 oder höher fest:

  1. Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf den Projektnamen, und wählen Sie "Eigenschaften" aus.
  2. Wählen Sie im linken Bereich des Fensters mit den Projekteigenschaftenseiten die Option Konfigurationseigenschaften>Allgemein aus.
  3. Wählen Sie im Dropdownmenü "C++-Sprachstandard" iso C++20 Standard (/std:c++20) oder höher aus. Wählen Sie 'OK' aus, um das Dialogfeld zu schließen.

Kompilieren sie die Headerdatei als Kopfzeileneinheit:

  1. Wählen Sie in Projektmappen-Explorer die Datei aus, die Sie als Kopfzeileneinheit kompilieren möchten (in diesem Fall). Pythagorean.h Klicken Sie mit der rechten Maustaste auf die Datei, und wählen Sie "Eigenschaften" aus.

  2. Legen Sie die Dropdownliste "Allgemeine Elementtyp" auf den C/C++-Compiler fest>>, und wählen Sie "OK" aus.

    Screenshot that shows changing the item type to C/C++ compiler.

Wenn Sie dieses Projekt später in dieser exemplarischen Vorgehensweise erstellen, Pythagorean.h wird sie in eine Kopfzeileneinheit übersetzt. Es wird in eine Kopfzeileneinheit übersetzt, da der Elementtyp für diese Headerdatei auf C/C++-Compiler festgelegt ist, und da die Standardaktion für .h und .hpp Dateien auf diese Weise die Datei in eine Kopfzeileneinheit übersetzen soll.

Hinweis

Dies ist für diese exemplarische Vorgehensweise nicht erforderlich, wird jedoch für Ihre Informationen bereitgestellt. Um eine Datei als Kopfzeileneinheit zu kompilieren, die nicht über eine Standarddateierweiterung für Headereinheit verfügt, z.cpp. B. legen Sie Konfigurationseigenschaften>C/C++>Advanced>Compile As to Compile as C++ Header Unit (/exportHeader) fest:Screenshot that shows changing Configuration properties > C/C++ > Advanced > Compile As to Compile as C++ Header Unit (/exportHeader).

Ändern des Codes zum Importieren der Kopfzeileneinheit

  1. Ändern Sie #include "Pythagorean.h"import "Pythagorean.h"; in der Quelldatei für das Beispielprojekt das nachfolgende Semikolon in "Nicht vergessen". Sie ist für import Anweisungen erforderlich. Da es sich um eine Headerdatei in einem lokalen Verzeichnis für das Projekt handeln soll, haben wir Anführungszeichen mit der import Anweisung verwendet: import "file"; Wenn Sie in Ihren eigenen Projekten eine Kopfzeile aus einem Systemkopf kompilieren möchten, verwenden Sie winkelige Klammern: import <file>;

  2. Erstellen Sie die Projektmappe, indem im Hauptmenü auf Erstellen>Projektmappe erstellen klicken. Führen Sie sie aus, um zu sehen, dass sie die erwartete Ausgabe erzeugt: Pythagorean triple a:2 b:3 c:13

Wiederholen Sie in Ihren eigenen Projekten diesen Vorgang, um die Headerdateien zu kompilieren, die Sie als Headereinheiten importieren möchten.

Wenn Sie nur einige Headerdateien in Kopfzeileneinheiten konvertieren möchten, ist dieser Ansatz gut. Wenn Sie jedoch viele Headerdateien haben, die Sie kompilieren möchten, und der potenzielle Verlust der Buildleistung wird durch den Komfort überwiegt, dass sie vom Buildsystem automatisch behandelt werden, lesen Sie den folgenden Abschnitt.

Wenn Sie speziell STL-Bibliotheksheader als Kopfzeilen importieren möchten, lesen Sie die exemplarische Vorgehensweise: Importieren von STL-Bibliotheken als Kopfzeileneinheiten.

Ansatz 2: Automatische Suche nach Kopfzeileneinheiten und Erstellen von Headereinheiten

Da das Scannen aller Quelldateien auf Kopfzeileneinheiten und die Zeit zum Erstellen dauert, eignet sich der folgende Ansatz am besten für kleinere Projekte. Es garantiert keinen optimalen Builddurchsatz.

Dieser Ansatz kombiniert zwei Visual Studio-Projekteinstellungen:

  • Scan Sources for Module Dependencies bewirkt, dass das Buildsystem den Compiler aufruft, um sicherzustellen, dass alle importierten Module und Headereinheiten erstellt werden, bevor die Dateien kompiliert werden, die von ihnen abhängig sind. In Kombination mit Translate Includes to Imports werden alle Headerdateien, die in Ihrer Quelle enthalten sind, die sich auch in einer header-units.json Datei im selben Verzeichnis wie die Headerdatei befinden, in Kopfzeileneinheiten kompiliert.
  • Translate Includes to Imports behandelt eine Headerdatei als import eine Kopfzeilendatei, die #include als Kopfzeileneinheit (wie in einer header-units.json Datei angegeben) kompiliert werden kann, und eine kompilierte Kopfzeileneinheit steht für die Headerdatei zur Verfügung. Andernfalls wird die Headerdatei als normal #includebehandelt. Die header-units.json Datei wird verwendet, um Kopfzeileneinheiten automatisch für jedes #include, ohne Symbolduplizierung zu erstellen.

Sie können diese Einstellungen in den Eigenschaften für Ihr Projekt aktivieren. Klicken Sie dazu im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen Sie "Eigenschaften" aus. Wählen Sie dann "Konfigurationseigenschaften>C/C++>Allgemein" aus.

Screenshot that shows the project properties screen with Configuration highlighted and All Configurations selected. Under C/C++ > General, Scan Sources for Module Dependencies is highlighted and set to yes, and Translate Includes to Imports is highlighted and set to Yes (/translateInclude)

Scanquellen für Modulabhängigkeiten können für alle Dateien im Projekt in Den Projekteigenschaften festgelegt werden, wie hier gezeigt, oder für einzelne Dateien in Dateieigenschaften. Module und Headereinheiten werden immer gescannt. Legen Sie diese Option fest, wenn Sie über eine .cpp Datei verfügen, die Headereinheiten importiert, die automatisch erstellt werden sollen und möglicherweise noch nicht erstellt werden.

Diese Einstellungen arbeiten zusammen, um Kopfzeileneinheiten unter diesen Bedingungen automatisch zu erstellen und zu importieren:

  • Scannen Von Quellen für Modulabhängigkeiten werden Ihre Quellen auf die Dateien und deren Abhängigkeiten überprüft, die als Kopfzeileneinheiten behandelt werden können. Dateien mit der Erweiterung .ixxund Dateien, die ihre Dateieigenschaften>C/C++>Compile As-Eigenschaft auf "Kompilieren als C++-Headereinheit (/export)" festgelegt haben, werden unabhängig von dieser Einstellung immer gescannt. Der Compiler sucht auch nach import Anweisungen, um Abhängigkeiten von Headereinheiten zu identifizieren. Wenn /translateInclude angegeben, sucht der Compiler auch nach #include Direktiven, die auch in einer header-units.json Datei angegeben sind, um sie als Headereinheiten zu behandeln. Ein Abhängigkeitsdiagramm basiert auf allen Modulen und Kopfzeileneinheiten in Ihrem Projekt.
  • Translate Includes to Imports When the compiler encounters an #include statement, and a matching header unit file (.ifc) exists for the specified header file, the compiler imports the header unit instead of treating the header file as an #include. In Kombination mit " Suchen nach Abhängigkeiten" findet der Compiler alle Headerdateien, die in Headereinheiten kompiliert werden können. Eine Zulassungsliste wird vom Compiler konsultiert, um zu entscheiden, welche Headerdateien in Kopfzeileneinheiten kompiliert werden können. Diese Liste wird in einer header-units.json Datei gespeichert, die sich im selben Verzeichnis wie die enthaltene Datei befinden muss. Sie können ein Beispiel für eine header-units.json Datei unter dem Installationsverzeichnis für Visual Studio sehen. Wird beispielsweise vom Compiler verwendet, um zu bestimmen, %ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.30.30705\include\header-units.json ob ein Standardvorlagenbibliotheksheader in eine Kopfzeile kompiliert werden kann. Diese Funktionalität ist vorhanden, um als Brücke mit Legacycode zu dienen, um einige Vorteile von Headereinheiten zu erhalten.

Die header-units.json Datei dient zwei Zwecken. Zusätzlich zur Angabe, welche Headerdateien in Kopfzeileneinheiten kompiliert werden können, minimiert es duplizierte Symbole, um den Builddurchsatz zu erhöhen. Weitere Informationen zur Symbolduplizierung finden Sie in der Referenz zu C++-Header-units.json.

Diese Schalter und einige header-unit.json der Vorteile von Kopfzeileneinheiten bieten. Der Komfort kommt zu den Kosten des Builddurchsatzes. Dieser Ansatz ist für größere Projekte möglicherweise nicht am besten geeignet, da es keine optimalen Buildzeiten garantiert. Außerdem können dieselben Headerdateien wiederholt verarbeitet werden, wodurch die Buildzeit erhöht wird. Der Komfort lohnt sich jedoch je nach Projekt.

Diese Features sind für Legacycode ausgelegt. Für neuen Code wechseln Sie zu Modulen anstelle von Headereinheiten oder #include Dateien. Ein Lernprogramm zur Verwendung von Modulen finden Sie im Lernprogramm für Namensmodule (C++).

Ein Beispiel für die Verwendung dieser Technik zum Importieren von STL-Headerdateien als Kopfzeileneinheiten finden Sie unter Walkthrough: Import STL libraries as header units.

Auswirkungen von Präprozessoren

Der C99/C++11-standardkonforme Präprozessor ist erforderlich, um Headereinheiten zu erstellen und zu verwenden. Der Compiler ermöglicht den neuen C99/C++11-konformen Präprozessor beim Kompilieren von Headereinheiten, indem sie der Befehlszeile implizit hinzufügen /Zc:preprocessor , wenn eine beliebige Form von /exportHeader Verwendet wird. Wenn Sie versuchen, sie zu deaktivieren, tritt ein Kompilierungsfehler auf.

Das Aktivieren des neuen Präprozessors wirkt sich auf die Verarbeitung variadischer Makros aus. Weitere Informationen finden Sie im Abschnitt "Variadische Makros ".

Siehe auch

/translateInclude
/exportHeader
/headerUnit
header-units.json
Vergleichen von Kopfzeileneinheiten, Modulen und vorkompilierten Headern
Übersicht über Module in C++
Lernprogramm: Importieren der C++-Standardbibliothek mithilfe von Modulen
Exemplarische Vorgehensweise: Importieren von STL-Bibliotheken als Headereinheiten