Projektstruktur: Header Dateien im seperaten include folder?



  • Hall 🙂

    Immer mal wieder lese ich Empfehlungen wie man denn am besten sein Projekt strukturieren sollte. Eine sehr häufige Empfehlung ist dabei, alle header Dateien in einem include verzeichnis aufzubewahren anstatt sie im selben Ordner wie die dazugehörige .cpp Datei zu haben.

    Nun wollte ich mal wissen, was hier so die Präferenz dafür ist. Welche Argumente gibt es für die eine oder die andere Strategie?

    Persönlich könnte ich mir kaum vorstellen mit einem extra include Verzeichnis zu arbeiten, ich denke die Suche immer nach den dazugehörigen header Dateien könnte auf Dauer etwas nervig sein (hier kann einem die IDE natürlich etwas helfen, das will ich aber mal nicht in Betracht hier jetzt ziehen).



  • @Leon0402 sagte in Projektstruktur: Header Dateien im seperaten include folder?:

    ich denke die Suche

    Warum sollte man da mehr suchen müssen?



  • Wenn ich links mein projekt als ne Baumstruktur habe, hab ich bei dem einem die .cpp und .h am selben Ort und beim anderen nicht.
    Sprich, wenn ich die .h oder die .cpp geöffnet habe, kann ich die dazugehörige .cpp/.h einfach öffnen, da sie genau unten drunter/oben drüber gelistet ist.
    Im Fall der Separierung muss ich die Datei erstmal dann in dem anderen Ordner suchen, um sie zu öffnen.
    Dass ich die .h brauche, wenn ich die .cpp offen habe (und dort porgrammier / Code lese) kommt zumindest bei mir dann doch recht oft vor. Oder natürlich auch andersrum.

    Dazu vlt. auch die Anschlussgfrage, falls ihr eine Struktur mit include verzeichnis wählt, würdet ihr die Struktur in dem Ordner identisch zu der im src folder machen?
    Spricht:

    - include 
      - Gui
        - Foo1.h 
        - Foo2.h 
      - Backend 
        - Foo3.h
    - src
      - Gui
        - Foo1.cpp
        - Foo2.cpp 
      - Backend 
        - Foo3.cpp
    


  • ja. Schon allein für einen selbst, sonst wird man ja ggf irre beim suchen.


  • Mod

    Ich mag es ehrlich gesagt lieber alles zusammen. Bei C sehe ich ja ein, dass eine Trennung Sinn macht, aber bei C++ steht so oft echter Programmcode im Header, dass die logisch nicht getrennt gehören. Man könnte zwar mit tcc-Dateien zur Implementierung von Templates anfangen, aber ich sehe das nur als Selbstzweck, dass man damit seine Dateien wieder in die Verzeichnisstruktur pressen kann, in die sie eigentlich nicht reinpassen. Gewonnen hat man damit nichts, weil Build- und Installationsskripte damit auch nicht einfacher werden, eher im Gegenteil (Vereinfachung von Build- und Installationsskripten ist für mich bei C der Grund, wieso dort eine Trennung Sinn macht)

    Vielleicht bin ich auch einfach zu faul.



  • Die Trennung macht Sinn, wenn man unsichtbare Header hat, die nicht zum "Interface" der Bibliothek gehören und nur internals enthalten. Ist zwar komisch sowas im Source Ordner zu haben, aber ¯\(ツ)/¯.

    für cmake ist für das install script das filtern mit ".h" oder ".hpp" sowieso fähig eine Trennung zu machen.
    Persönlich benutze ich make install aber oft gar nicht.
    Dann den include ordner als Suchordner hinzufügen ist offensichtlich, deshalb praktisch.



  • Da es hier noch keiner erwähnt hat... Ich benutze meist Visual Studio, und da gibts auf Tabs eine Funktion, die Datei im Explorer zu öffnen. Da ist es natürlich sehr praktisch, wenn die Header und cpp Dateien in einem Verzeichnis liegen.



  • @Mechanics Ich wollte es zuerst nicht erwähnen weil @Leon0402 IDE-Unterstützung explizit ausgeschlossen hat.

    Hat man ein Projekt in dem man auch incude-files für externe Verwendung hat ist ein separates Verzeichnis dafür sicher nicht kerfehrt.



  • Ich hatte seinen Satz über die IDE etwas anders interpretiert. Mit Visual Studio (zumindest mit Visual Assist) kann ich zwischen Header und Cpp springen, auch wenn die in unterschiedlichen Verzeichnissen liegen. So habe ich das interpretiert.
    Ist aber nochmal ein etwas anderer Punkt, wenn man sich die Dateien tatsächlich mal im Explorer anschauen will, und z.B. löschen, zur Versionsverwaltung hinzufügen (mach ich nicht in VS) oder ähnliches.

    Es gibt übrigens Projekte, die haben einen install step oder ähnliches, der dann includes in ein separates Verzeichnis rauskopiert. Glaube ich zumindest, würde jetzt aber nicht ausschließen, dass ich nicht genau genug hingeschaut habe.



  • @Mechanics sagte in Projektstruktur: Header Dateien im seperaten include folder?:

    Ist aber nochmal ein etwas anderer Punkt, wenn man sich die Dateien tatsächlich mal im Explorer anschauen will, und z.B. löschen, zur Versionsverwaltung hinzufügen (mach ich nicht in VS) oder ähnliches.

    Dann klicke ich auf "Open Folder in File Explorer", doppelklicke auf "include" und lösche die Datei.



  • Du meinst, wenn die includes in einem Unterverzeichnis der cpp´s liegen?



  • @Mechanics Ich? ja.



  • Ja, das würde noch gehen. Wir haben bei uns teilweise Verzeichnisstrukturen, die in etwa so aufgebaut sind:

    root

    • includes
      • a
        • b
    • source
      • a
        • b

    D.h., wenn man den Ordner der cpp Datei öffnet, befindet man sich in der Verzeichnisstruktur ganz woanders... Und wie das genau aufgebaut ist, ist auch nicht ganz einheitlich, d.h. einfach im Pfad noch includes eintippen klappt auch nicht immer.



  • Generell wollte ich hier mal IDE Unterstützung ausschließen, da jeder einen anderen Editor verwendet und andere plugins etc. ... sowas für ein größeres Projekt zu erwarten, dass jeder diese tools hat (und auch gerne benutzt) würde ich denke ich nicht so gut finden.

    Hat man ein Projekt in dem man auch incude-files für externe Verwendung hat ist ein separates Verzeichnis dafür sicher nicht kerfehrt.

    Also uneinheitlich? Nur das public interface in das include verzeichnis stecken und private header etc. im src folder lassen?
    Diese Variante, so logisch ich sie irgendwo auch finde, würde mich vermutlich am meisten verwirren.



  • Bei einer Library macht dies aber Sinn, da man so das gesamte (public interface) "include"-Verzeichnis mitausliefern kann (ohne explizit die einzelnen benötigten Headerdateien zusammenkopieren zu müssen).

    Und wenn man bei einem eigenständigen Projekt dann evtl. einen Teil davon als Library auslagern möchte, müßte man (höchstwahrscheinlich) die Struktur ersteinmal umbauen (z.B. relative Pfade anpassen).


  • Mod

    Der Knackpunkt ist aber trotzdem noch, was du mit den Templates machst, wenn diese Teil des öffentlichen Interfaces sind. Packst du sie mit in einen separaten Ordner für das öffentliche Interface, ist das hinderlich bei der Entwicklung, weil dir dann die Trennung nichts mehr bringt außer einer selbstgemachten Verkomplizierung. Packst du sie mit in einen Sourcefolder macht das auch wieder den Sinn eines separaten Interfaceordners zunichte, weil du dann am Ende doch wieder aufpassen musst, welche Teile wirklich zur Funktion des öffentlichen Interfaces notwendig sind.

    Das Grundproblem ist, dass man in C++ sprachbedingt Teile hat, die gleichzeitig sowohl Interface als auch Wirkcode sind. Das Problem, alles in diese zwei Kategorien aufzuteilen kann man daher nicht zufriedenstellend lösen, außer durch komplizierte Build-Magic, die das Problem versteckt (Oder ich kenne die geniale Lösung nicht. Ich wäre für Lösungen dankbar!)



  • Da die Templates Teil des öffentlichen Interfaces sind, gehören sie selbstverständlich auch in diesen Include-Ordner (entweder direkt in den Headerdateien oder aber eben ausgelagert, z.B. in einem Unterordner davon und per #includeeingebunden).

    Ich verstehe aber anscheinend das Problem nicht. Jede vernünftige IDE bietet doch heute entsprechende Funktionalität zum Wechseln zwischen Source und Header (bei gleichem Namen bzw. explizit über Öffnen der Headerdatei beim #include).
    Und wenn ich an einer Klasse arbeite, dann öffne ich einmalig die beiden Dateien in 2 Tabs nebeneinander (und diese bleiben dann auch beim nächsten Projektöffnen erhalten).



  • @Th69 sagte in Projektstruktur: Header Dateien im seperaten include folder?:

    Bei einer Library macht dies aber Sinn, da man so das gesamte (public interface) "include"-Verzeichnis mitausliefern kann (ohne explizit die einzelnen benötigten Headerdateien zusammenkopieren zu müssen).

    Hierzu noch eine Empfehlung: Es macht Sinn, dass das include-Verzeichnis des Projekts genau so strukturiert ist, wie es im globalen include-Verzeichnis nach der Installation aussehen soll.

    Also statt include/mylib.hpp lieber include/myproject/mylib.hpp.

    So kann ich innerhalb des Projekts einfach include als Include-Verzeichnis angeben und die Header dann genau so verwenden, wie es letztendlich auch der Endanwender tut, also z.B. mit #include <myproject/mylib.hpp>.

    Die Trennung von Header und Source in unterschiedliche Verzeichnisse empfinde ich in den IDEs, die ich verwende als unproblematsich, selbst ohne die Funktion zu nutzen, mit der ich zwischen Deklaration und Definition wechseln kann. Ich verwende in meinen Projekten oft viele weitere Unterverzeichnise unterhalb von include und source, so dass ich auch bei großen Projekten meist nur eine Handvoll Dateien pro Verzeichnis habe. In der Projektübersicht habe ich dann meist nur die relavanten Unterverzeichnisse aufgeklappt, so dass ich die zugehörigen Dateien sehr schnell finde. Ich denke auch mit "Lowlevel-IDEs" wie vim sollte so etwas möglich sein (?).

    @SeppJ Diese Verquickung von "Interface" und "Wirkcode" ist aber m.E. keine Notwendigkeit, sondern lediglich eine Art syntaktischer Zucker, der einem Tipparbeit erspart. Die Aufteilung von Interface und Implementierung in separate Dateien ist immer möglich.

    Nicht vermeidbar hingegen ist, dass es "Wirkcode" gibt, der einer Übersetzungseinheit vollständig bekannt sein muss, wenn sie diesen verwenden will (Template- oder inline-Funktionen). Diese Sichtweise führt aber leider auch nicht zu einer guten Lösung, sondern eher zu sowas:

    include_headers
    include_source
    source
    

    Wurgs! Das mag zwar die tatsächlichen Zusammenhänge besser auf die Verzeichnisstruktur abbilden, ist aber nicht wirklich ein Mehrwert oder irgendwie gut handhabbar.

    Ich hoffe ja, das Modules das irgendwann alles weitgehend überflüssig machen werden 😉



  • @SeppJ sagte in Projektstruktur: Header Dateien im seperaten include folder?:

    Das Problem, alles in diese zwei Kategorien aufzuteilen kann man daher nicht zufriedenstellend lösen, außer durch komplizierte Build-Magic, die das Problem versteckt (Oder ich kenne die geniale Lösung nicht. Ich wäre für Lösungen dankbar!)

    Weiss nicht was daran speziell kompliziert ist. Alles was extern benötigt wird kommt ins "include" Verzeichnis und alles was nicht extern benötigt wird ins "src" Verzeichnis. Man darf die auch gerne anders nennen, z.B. eins "public" und eins "impl", "private", "internal" oder was auch immer.

    Wie sinnvoll das ganze ist kommt dann natürlich immer drauf an. Also wenn fast alles in Templates ist oder inline sein muss wegen constexpr, dann macht es klar weniger Sinn. Gibt aber Projekte wo überraschend wenig übrig bleibt wenn man konsequent alles wo es (bewiesenermassen, Profiler) wörscht ist aus den .h Files raus in die .cpp-s verschiebt.
    (Bzw. oft hat das Verschieben ins .cpp sogar Vorteile. Compiler sind oft viel zu aggressiv beim Inlinen und inlinen dummen "kalten" Fehlerbehandlungscode 10 Ebenen tief und müllen damit den ICache zu.)



  • @hustbaer sagte in Projektstruktur: Header Dateien im seperaten include folder?:

    Gibt aber Projekte wo überraschend wenig übrig bleibt wenn man konsequent alles wo es (bewiesenermassen, Profiler) wörscht ist aus den .h Files raus in die .cpp-s verschiebt.

    Ich würde auch sagen, dass sowas haupstächlich in Bibliotheken Anwendung finden sollte. Vor allem, weil diese nicht wissen können, in welchem Kontext sie letztendlich verwendet werden und es eben nicht "wörscht" sein könnte. In Anwendungscode habe ich selbst auch nur selten Bedarf für inline oder exzessive Nutzung von Templates.

    (Bzw. oft hat das Verschieben ins .cpp sogar Vorteile. Compiler sind oft viel zu aggressiv beim Inlinen und inlinen dummen "kalten" Fehlerbehandlungscode 10 Ebenen tief und müllen damit den ICache zu.)

    Ich würde nicht versuchen, dieses Verhalten über das inline-Schlüsselwort zu steuern. Wenn sich hier tatsächlich ein Problem zeigt, erachte ich es als sinnvoller, das mit compilerspezifischen noinline-Mechanismen zu erschlagen:

    Erstens, weil sich die Bedeutung von inline mittlerweile von "bitte inlinen" zu "mehrfache Definitionen sind erlaubt" gewandelt hat (siehe auch z.B. inline-Variablen, wo die ursprüngliche Bedeutung von inline nicht wirklich Sinn macht).

    Zweitens, weil Compiler auch nicht mit inline markierte Funktionen und - sofern man z.B. LTO verwendet - auch Funktionen aus verschiedenen .cpp-Übersetzungseinheiten programmübergreifend inlinen.


Log in to reply