Includes in cpp Datei verlagern



  • Hallo,

    soweit ich mich erinnern kann, ist es doch empfehlenswert die #includes nach Möglichkeit in den .cpp Dateien zu haben. Die Begründung dafür ist meines Wissens nach die selbe, warum man kein using namespace ... in header verwendnen sollte, da die header Datei inkludiert wird und somit alle nachfolgenden Dateien das auch inkludieren indirekt. Hier erstmal die Frage, stimmt das? Sollte man die includes so gut es geht in die .cpp verlagern?

    Nun stellt sich natürlich noch die Frage, wann kann man das verlagern. Ich habe immer wieder festgestellt, dass manchmal eine forward decleration genug ist und manchmal eben nicht, wenn die Klasse in der header verwendet wird (Rückgabetyp von Methoden, paramter, member etc.).
    Gibt es da eine Regel, wann die forward decleration ausreichend ist?

    Grüße
    Leon



  • Vorwärtsdeklaration reicht aus bei

    • Zeiger/Referenzen
    • friend
    • Funktionsdeklarationen ohne default Parameter


  • Das wirkt sich teilweise sehr gravierend auf die Übersetzungszeit aus. Je weniger Code und includes man in Headern hat, desto besser. Hab früher beim Aufräumen öfter mal Faktor 5 beobachtet.



  • Danke erstmal für eure Antworten.
    Ich werde das ganze mal ausprobieren mit der Forward Decleration und dann verinnerlichen. Doch sehr interessant finde ich, dass die Kompilierzeit so drastisch verringert wird.
    Woran liegt das?

    Kompiliert wird ja bei bei beidem nur einmal und auch nur einmal eingelesen. Das einzige was sich ändert ist die Zeit, um die Header Datei einzubinden, weil sie natürlich größer dadurch ist. Aber ich dachte in Vergleich zur Zeit fürs Kompilieren, spielt das kaum ne Rolle. Oder täusche ich mich da oder übersehe was?



  • @Leon0402 sagte in Includes in cpp Datei verlagern:

    Danke erstmal für eure Antworten.
    Ich werde das ganze mal ausprobieren mit der Forward Decleration und dann verinnerlichen. Doch sehr interessant finde ich, dass die Kompilierzeit so drastisch verringert wird.
    Woran liegt das?

    Kompiliert wird ja bei bei beidem nur einmal und auch nur einmal eingelesen. Das einzige was sich ändert ist die Zeit, um die Header Datei einzubinden, weil sie natürlich größer dadurch ist. Aber ich dachte in Vergleich zur Zeit fürs Kompilieren, spielt das kaum ne Rolle. Oder täusche ich mich da oder übersehe was?

    Ich sollte das in meinem großen QT-Frontend auch mal versuchen... Nachteil ist natürlich dass man jedes einzelne QT-Widget im Header forwarden muss...



  • @It0101 sagte in Includes in cpp Datei verlagern:

    @Leon0402 sagte in Includes in cpp Datei verlagern:

    Danke erstmal für eure Antworten.
    Ich werde das ganze mal ausprobieren mit der Forward Decleration und dann verinnerlichen. Doch sehr interessant finde ich, dass die Kompilierzeit so drastisch verringert wird.
    Woran liegt das?

    Kompiliert wird ja bei bei beidem nur einmal und auch nur einmal eingelesen. Das einzige was sich ändert ist die Zeit, um die Header Datei einzubinden, weil sie natürlich größer dadurch ist. Aber ich dachte in Vergleich zur Zeit fürs Kompilieren, spielt das kaum ne Rolle. Oder täusche ich mich da oder übersehe was?

    Ich sollte das in meinem großen QT-Frontend auch mal versuchen... Nachteil ist natürlich dass man jedes einzelne QT-Widget im Header forwarden muss...

    Du bringst es auf den Punkt! Das war Hintergrund meiner Frage 😃
    Aber natürlich ist es auch generell einfach interessanr zu wissen



  • @Leon0402
    Wenn ich Montag wieder im Büro bin, werde ich das mal bei meinem großen QT-Frontend versuchen und die Zeiten vorher und nachher mal messen. Wird aber paar Stunden dauern bis ich überall forwards eingebaut habe. 😀



  • @It0101 sagte in Includes in cpp Datei verlagern:

    @Leon0402
    Wenn ich Montag wieder im Büro bin, werde ich das mal bei meinem großen QT-Frontend versuchen und die Zeiten vorher und nachher mal messen. Wird aber paar Stunden dauern bis ich überall forwards eingebaut habe. 😀

    Zähl mal auch mit, wie viele includes du in die cpp verlagert hast. Dann wird die Statistik sehr interessant 😉



  • Es würde ja auch reichen, eine Header-Datei mit den Vorwärtsdeklarationen zu erstellen ("qt-forward.h").
    Es gibt extra (dafür) die Makros: QT_FORWARD_DECLARE_CLASS sowie QT_BEGIN_NAMESPACE/QT_END_NAMESPACE.



  • @Th69 sagte in Includes in cpp Datei verlagern:

    Es würde ja auch reichen, eine Header-Datei mit den Vorwärtsdeklarationen zu erstellen ("qt-forward.h").
    Es gibt extra (dafür) die Makros: QT_FORWARD_DECLARE_CLASS sowie QT_BEGIN_NAMESPACE/QT_END_NAMESPACE.

    Mein erster Ansatz war jetzt, für jeden Header spezifisch zu forwarden.
    Du meinst das aber jetzt sicher so, dass man sich genau einen Header mit forwards macht und den überall in Headern includet? Forwards fressen ja an sich kein Brot, denke ich mal, daher könnte das tatsächlich gehen, auch wenn natürlich immer welche dabei sind, die in einem Header unnütz sind.



  • Ja, genau so meinte ich es. Der Aufwand bei jedem Header explizit die Vorwärtsdeklarationen zu erstellen ist größer als einfach nur jeweils #include "qt-forward.h" zu benutzen.
    Wenn man dann in einem großen Projekt doch Hunderte von Vorwärtsdeklarationen (für fast alle Qt-Widgets und -Klassen) benutzt, dann kann man immer noch diese aufteilen.



  • Ich bin gerade dabei, mein altes QT-Projekt umzustrukturieren. Es ist jedenfalls ne Menge Arbeit und der Compiler wirft einem ein paar Steine in den Weg 😉

    Wo forwards in einem Header nicht eingesetzt werden können:

    • Basisklassen ( Klassen von denen ich ableite )
    • Member die als Instanz vorliegen und nicht als Pointer
    • enums kann man nicht forwarden

    Probleme die bei mir bisher aufgetreten sind ( nicht allgemeingültig ):

    • komplexere typedefs ( z.B. typedef std::shared_ptr<MyClass> MyOftenUsedPointer; ). Solche typedefs benötigen noch für ihre eigene Existenz mindestens zwei Header. Diese typedefs muss man einer geeigneten Stelle platzieren.
    • Enums die im public-Teil einer Klasse definiert werden und in einer anderen Klasse im Header benötigt werden.

    Für das Enumproblem fällt mir aktuell keine gescheite Lösung ein, außer jedes Enum in einen eigenen Header zu verfrachten, oder einen Sammelheader für Enums zu machen. Die zweite Lösung ist eher unschön, weil enums mitunter Klassenspezifisch sind und zu einer Klasse gehören. Die erste Lösung ist ne Menge Aufwand und produziert jede Menge Header.

    Besonders nervig sind Enums, die in einem externen Modul innerhalb einer Klasse definiert sind, weil man auf dieses Modul quasi keinen Einfluss hat, man die Definition des Enums aber für die eigenen KlassenDefintion benötigt.

    Eine Idee wäre, die externen Module/Klassen generell nicht zu forwarden, sondern in einen pch zu verschieben.



  • Ich habe für ein embedded Projekt die Include Bäume extrahiert (als Teil von compile time optimization) und dann zeichnen lassen.
    Die generierten SVG Dateien wurden teilweise wahnwitzig riesig, und das allein durch ein paar boost header. Und das bei den kleinen Dateien, wo nicht viel included wird, die großen konnte ich gar nicht mehr zeichnen.
    Also ja, ein "harmloser" include kann zu einem wahren Monstrum werden.
    Also am besten so viel in Sourcen ziehen wie es geht.

    Ein "harmloses" Beispiel



  • @5cript sagte in Includes in cpp Datei verlagern:

    Also am besten so viel in Sourcen ziehen wie es geht.

    Das ist schon klar. Nur ist halt das Problem, dass man auf einige include-header in der header Datei nicht verzichten kann...



  • @It0101 sagte in Includes in cpp Datei verlagern:

    @5cript sagte in Includes in cpp Datei verlagern:

    Also am besten so viel in Sourcen ziehen wie es geht.

    Das ist schon klar. Nur ist halt das Problem, dass man auf einige include-header in der header Datei nicht verzichten kann...

    Ich möchte nur unterstreichen, dass man oft sich gar nicht ausmalt was ein Präprozessor und Compiler leistet und was man seiner armen Toolchain antut.



  • @5cript sagte in Includes in cpp Datei verlagern:

    @It0101 sagte in Includes in cpp Datei verlagern:

    @5cript sagte in Includes in cpp Datei verlagern:

    Also am besten so viel in Sourcen ziehen wie es geht.

    Das ist schon klar. Nur ist halt das Problem, dass man auf einige include-header in der header Datei nicht verzichten kann...

    Ich möchte nur unterstreichen, dass man oft sich gar nicht ausmalt was ein Präprozessor und Compiler leistet und was man seiner armen Toolchain antut.

    Das sollte mal als Hinweis in jede IDE eingebaut werden 😉



  • @5cript sagte in Includes in cpp Datei verlagern:

    ein paar boost header

    Ja, sind bei uns auch stets der Compile-time-killer.
    Haben sogar Klassen um-designed, um boost aus headern rauszukriegen.



  • @It0101 sagte in Includes in cpp Datei verlagern:

    enums kann man nicht forwarden

    AFAIK kann man enums forwarden, wenn der underlying type explizit angegeben wird:

    enum Foo : int;
    


  • @LeMace sagte in Includes in cpp Datei verlagern:

    @It0101 sagte in Includes in cpp Datei verlagern:

    enums kann man nicht forwarden

    AFAIK kann man enums forwarden, wenn der underlying type explizit angegeben wird:

    enum Foo : int;
    

    Ok, das war mir nicht bekannt. Guter Hinweis.



  • @5cript sagte in Includes in cpp Datei verlagern:

    Ich möchte nur unterstreichen, dass man oft sich gar nicht ausmalt was ein Präprozessor und Compiler leistet und was man seiner armen Toolchain antut.

    Habe mir auch schon oft gedacht, ob es wirklich sinnvoll ist, wenn Build-Systeme für jede Übersetzungseinheit einen neuen Compiler-Prozess starten, der nichts von dem weiss, was vorher gewesen ist.

    Ich frage mich, warum wir nach all der Zeit nicht schon Toolchains haben, bei denen der oder die Compiler-Prozesse einmal gestartet, und dann vom Build-System "ferngesteuert" werden, so dass diese einmal verarbeitete Header wiederverwenden können.Das wäre natürlich ein deutlich komplexeres System, aber die Geschwindigkeitsvorteile könnten enorm sein. Precompiled Header sind zwar auch eine Lösung, erfordern aber meist Anpassungen am Code und müssen auch für jede Übersetzungseinheit zumindest "geladen" werden.

    Oder gibt es sowas in die Richtung vielleicht sogar schon? ist mir zumindest bisher noch nicht aufgefallen, so wie die Build-Systeme, die ich kenne funktionieren.