Warum ein include/-Verzeichnis in C- und C++-Projekten erstellen?

Lesezeit: 7 Minuten

Wenn ich an meinen persönlichen C- und C++-Projekten arbeite, setze ich normalerweise file.h und file.cpp im selben Verzeichnis und dann file.cpp verweisen kann file.h mit einer #include "file.h" Richtlinie.

Es ist jedoch üblich, Bibliotheken und andere Arten von Projekten (wie den Linux-Kernel und freeRTOS) zu finden, in denen alle vorhanden sind .h Dateien werden innerhalb einer abgelegt include/ Verzeichnis, während .cpp Dateien verbleiben in einem anderen Verzeichnis. Bei diesen Projekten .h Dateien sind ebenfalls enthalten #include "file.h" Anstatt von #include "include/file.h" wie ich gehofft hatte.

Zu all dem habe ich einige Fragen:

  1. Was sind die Vorteile dieser Dateistrukturorganisation?
  2. Warum sind .h Dateien drin include/ inbegriffen #include "file.h" Anstatt von #include "include/file.h"? Ich weiß, dass der eigentliche Trick in einigen Makefiles steckt, aber ist es wirklich besser, dies auf diese Weise zu tun, anstatt (im Code) klarzustellen, dass die Datei, die wir einschließen möchten, tatsächlich in der include/ Verzeichnis?

  • Wenn so organisiert, include ist die Wurzel von Includes, daher ist es nicht Teil des eigentlichen Include-Namens. Normalerweise – aber nicht immer – haben Sie auch Quelldateien in a src Verzeichnis, btw.

    – Spektren

    1. Februar 2018 um 3:15 Uhr


  • Dieser Weg ist flexibler, Sie können den Speicherort von Header-Dateien ändern und müssen diese Tatsache nur an einer Stelle aktualisieren, stellen Sie sich den Wahnsinn vor, alle Dateien im Kernel durchgehen zu müssen, die eine Bibliothek verwenden, wenn Sie sich entscheiden, das Verzeichnis zu ändern Struktur und musste die Includes einzeln modifizieren. Das spart auch viel Tipparbeit.

    – Raúl Sauco

    1. Februar 2018 um 3:19 Uhr

  • Können Sie sich vorstellen, wie es wäre, wenn jeder Systemheader in einem anderen Verzeichnis gefunden werden müsste? Chaos fängt nicht an, es zu beschreiben. Deshalb werden sie in einem oder wenigen Verzeichnissen gesammelt.

    – Jonathan Leffler

    1. Februar 2018 um 3:19 Uhr

Benutzeravatar von Nicol Bolas
Nicol Bola

Der Hauptgrund dafür ist, dass kompilierte Bibliotheken Header benötigen, um vom späteren Benutzer verwendet werden zu können. Vereinbarungsgemäß ist der Inhalt der include Verzeichnis sind die Header, die für den öffentlichen Gebrauch verfügbar gemacht werden. Das Quellverzeichnis kann Header für den internen Gebrauch haben, aber das sind sie nicht soll mit der kompilierten Bibliothek verteilt werden.

Wenn Sie also die Bibliothek verwenden, verknüpfen Sie sie mit der Binärdatei und fügen die der Bibliothek hinzu include Verzeichnis zu den Headerpfaden Ihres Build-Systems. Wenn Sie Ihre kompilierte Bibliothek an einem zentralen Ort installieren, können Sie auf ähnliche Weise feststellen, welche Dateien an den zentralen Ort kopiert werden müssen (die kompilierten Binärdateien und die include Verzeichnis) und welche Dateien nicht (das Quellverzeichnis und so weiter).

  • Um das noch hinzuzufügen … Es ist eine Möglichkeit, Ihnen Modularisierung und Design-by-Contract zu geben. Typischerweise enthalten die Dateien im include-Verzeichnis auch Kommentare, die das Bibliotheksverhalten dokumentieren (Doxygen ist dafür beliebt). C# hat das Konzept der partiellen Klassendefinition, was es einfacher macht, die Implementierung verborgen zu halten, aber Sie müssen immer noch entscheiden, was die externe Schnittstelle sein soll und was rein intern in der Bibliothek sein soll.

    – Graham

    1. Februar 2018 um 10:47 Uhr

Früher war das so <header> Style Includes waren vom impliziten Pfadtyp, d. h. auf dem zu finden includes Umgebungsvariablenpfad oder ein Build-Makro und die "header" Stileinbindungen hatten die explizite Form, wie-in, genau relativ zu der Quelldatei, in der sie enthalten war. Während einige Build-Toolketten diese Unterscheidung noch zulassen, verwenden sie häufig standardmäßig eine Konfiguration, die sie effektiv aufhebt.

Ihre Frage ist interessant, weil sie die Frage aufwirft, was wirklich besser ist, implizit oder explizit? Die implizite Form ist sicherlich einfacher, weil:

  1. Praktische Gruppierungen zusammengehöriger Header in Verzeichnishierarchien.
  2. Sie müssen nur ein paar Verzeichnisse in die Datei aufnehmen includes Pfad und müssen nicht jedes Detail in Bezug auf die genauen Speicherorte von Dateien kennen. Sie können Versionen von Bibliotheken und ihren zugehörigen Headern ändern, ohne den Code zu ändern.
  3. TROCKEN.
  4. Flexibel! Ihre Build-Umgebung muss nicht mit meiner übereinstimmen, aber wir können oft fast genau die gleichen Ergebnisse erzielen.

Explicit hingegen hat:

  1. Wiederholbare Builds. Eine Neuordnung von Pfaden in einer includes Makro-/Umgebungsvariable, ändert keine resultierenden Header-Dateien, die während des Builds gefunden wurden.
  2. Tragbare Konstruktionen. Verpacken Sie einfach alles von der Wurzel des Builds und versenden Sie es an einen anderen Entwickler.
  3. Nähe von Informationen. Sie wissen genau, wo der Header mit ist #include "\X\Y\Z". In der impliziten Form müssen Sie möglicherweise mehrere Pfade durchsuchen und finden möglicherweise sogar mehrere Versionen derselben Datei. Woher wissen Sie, welche im Build verwendet wird?

Entwickler streiten seit vielen Jahrzehnten über diese beiden Ansätze, aber eine Hybridform der beiden gewinnt meistens aufgrund des Aufwands, der erforderlich ist, um Builds zu verwalten, die ausschließlich auf der expliziten Form basieren, und der offensichtlichen Schwierigkeit, sich mit Code vertraut zu machen rein impliziter Natur. Wir alle verstehen im Allgemeinen, dass unsere verschiedenen Toolketten bestimmte gemeinsame Bibliotheken und Header an bestimmten Orten ablegen, sodass sie von Benutzern und Projekten gemeinsam genutzt werden können. Daher erwarten wir, Standard-C/C++-Header an einem Ort zu finden, aber das tun wir zunächst nicht wissen nichts über die spezifische Struktur eines beliebigen Projekts, da es an einer lokal gut dokumentierten Konvention mangelt, also erwarten wir, dass der Code in diesen Projekten explizit in Bezug auf die nicht standardmäßigen Bits ist, die für sie einzigartig sind, und implizit in Bezug auf die Standardbits.

Es ist eine gute Praxis, immer die zu verwenden <header> Form des Include für alle Standard-Header und andere Bibliotheken, die nicht projektspezifisch sind und die verwendet werden sollen "header" Formular für alles andere. Sollten Sie in Ihrem Projekt ein Include-Verzeichnis für Ihre lokalen Include-Dateien haben? Das hängt bis zu einem gewissen Grad davon ab, ob diese Header als Schnittstellen zu Ihren Bibliotheken geliefert oder lediglich von Ihrem Code verwendet werden, und auch von Ihren Einstellungen. Wie groß und komplex ist Ihr Projekt? Wenn Sie eine Mischung aus internen und externen Schnittstellen oder vielen verschiedenen Komponenten haben, möchten Sie die Dinge vielleicht in separaten Verzeichnissen gruppieren.

Denken Sie daran, dass die Verzeichnisstruktur, in die Ihr fertiges Produkt entpackt wird, nicht so aussehen muss wie die Verzeichnisstruktur, unter der Sie dieses Produkt entwickeln und bauen. Wenn Sie nur wenige .c/.cpp-Dateien und Header haben, ist es in Ordnung, sie zu platzieren Sie alle in einem Verzeichnis, aber irgendwann werden Sie an etwas nicht Trivialem arbeiten und müssen die Konsequenzen Ihrer Build-Umgebungsentscheidungen durchdenken und es hoffentlich dokumentieren, damit andere es verstehen können.

  • Ich denke, das ist eine sehr umfassende Antwort. Ich füge hinzu, dass wir auch oft Hybriden verwenden – alle Header unserer Bibliotheken, die eine externe Verknüpfung benötigen (was sie tatsächlich bereitstellen), sind in einem Header-Verzeichnis gruppiert – sodass sie einfach als Block verschoben werden können – was häufig eine Voraussetzung für ” Installieren”. Header, die nur intern benötigt werden, werden explizit mit der Quelle verknüpft und mit der Quelle gespeichert, um die Quelle portabel zu halten und die Informationen zu gruppieren.

    – Kabanus

    1. Februar 2018 um 6:54 Uhr


  • Die Antwort ist gut, obwohl es für mich nicht so sehr “explizit vs. implizit” als “harcodiert vs. konfigurierbar” ist. Zum Beispiel wird „explicit.1“ annulliert, weil kein ernsthaftes Projekt ohne ein Build-System auskommt, und es dann ganz natürlich ist, dass das Build-System mit Pfaden umgeht. Gleiches gilt für Explicit.2, Sie würden nicht nur die Quellen packen, Sie packen das Ganze, Header, src, Daten, Build-Setup, sodass es keine Rolle spielt, ob sich Header in einem anderen Verzeichnis innerhalb des Pakets befinden.

    – Spektren

    1. Februar 2018 um 10:46 Uhr

1 . .hpp und .cpp müssen nicht unbedingt eine 1-zu-1-Beziehung haben, es kann mehrere .cpp-Dateien geben, die dieselbe .hpp-Datei verwenden, je nach unterschiedlichen Bedingungen (z. B.: unterschiedliche Umgebungen), zum Beispiel: eine Bibliothek für mehrere Plattformen, stellen Sie sich vor, es gibt eine Klasse um die Version der App zu erhalten, und der Header sieht so aus:

Dienstprogramme.h

#include <string.h>
class Utilities{
    static std::string getAppVersion();
}

main.cpp

#include Utilities.h
int main(){
    std::cout << Utilities::getAppVersion() << std::ends;
    return 0;
}

es kann eine .cpp für jede Plattform geben, und die .cpp kann an verschiedenen Orten platziert werden, so dass sie leicht von der entsprechenden Plattform ausgewählt werden können, z. B.:

.cpp für iOS (Pfad:DemoProject/ios/Utilities.cpp):

#include "Utilities.h"
std::string Utilities::getAppVersion(){
    //some objective C code
}

.cpp für Android (Pfad:DemoProject/android/Utilities.cpp):

#include "Utilities.h"
std::string Utilities::getAppVersion(){
    //some jni code
}

und natürlich würden 2 .cpp normalerweise nicht gleichzeitig verwendet werden.


2.

#include "file.h" 

Anstatt von

#include "include/file.h" 

ermöglicht es Ihnen, den Quellcode unverändert zu lassen, wenn Ihre Header nicht mehr im “include”-Ordner abgelegt werden.

1411040cookie-checkWarum ein include/-Verzeichnis in C- und C++-Projekten erstellen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy