#include <string> mit in den Header oder in der main.cpp ?



  • Die "gegenteilige Regel" ist eher eine Ausnahme für größere Projekte, um die Compile-Zeiten in den Griff zu bekommen:

    Wenn für den Header selbst eine Forward-Deklaration genügt, und clients die string-Klasse nicht unbedingt brauchen, sollte der Header nicht mit #includiert werden.

    Beispiel:

    // foo.h
    class Something;  // forward declarations
    class OtherThing;
    
    class CMyClass
    {
      public void SetSomething(Something const & sth);
      public void SetOther(OtherThing const & oth);
    };
    

    Wenn es möglich/häufig ist, das ein Client dieses Headers Something braucht, aber OtherThing nicht, hat man mindestens eine Abhängigkeit weniger (hinzu kommt ja noch alles, was von <something.h> reingeladen wird usw.).

    Die Compile-Zeiten können durch sowas beträchtlich sinken, das wird ab bestimmten Projektgrößen ntwendig, um die administrative Seite im Griff zu bekommen.

    Als erste Regel bleiben aber self-contained header.



  • Was ich mir noch vorstellen könnte wäre, dass man nur forward Deklarirungen in den Header packt.



  • sollte man in dem Fall unbedingt machen - aber man muß halt trotzdem dokumentieren, da clients u.U. noch weitere header benötigen



  • Die "gegenteilige Regel" ist eher eine Ausnahme für größere Projekte, um die Compile-Zeiten in den Griff zu bekommen:
    [...]

    Was ich mir noch vorstellen könnte wäre, dass man nur forward Deklarirungen in den Header packt.

    Äh, hallo?
    1. Seit wann kann man std::string forward deklarieren? -> Richtig. Seit gar nicht.
    2. Wo steht, dass self-contained forward Deklaration ausschließt? Ganz im Gegenteil. Wenn eine forward-declaration reicht, verwendet man eine forward-declaration und folgt der Regel "vermeide Includes in Headern"(und das macht man hoffentlich nicht nur bei großen Projekten)
    Self-contained heißt, dass ein Header "top-most" inkludiert werden kann, nicht dass man sinnlos irgendwelche zusätzlichen Header dort inkludiert.

    Ich frage mich manchmal echt ob ich eine andere Sprache spreche als ihr.



  • namespace std
    {
      template <class _E> struct char_traits;
      template <class _E> class allocator;
    
      template<class _E, class _Tr = char_traits<_E>, class _A=allocator<_E> > class basic_string;
    
      typedef basic_string<char> string;
    }
    
    void foo(std::string const &);
    // ...
    #include <string>
    void foo(std::string const & s)
    {
        printf(s.c_str());
    }
    

    Ist nicht 100% portabel, aber geht.

    Ich frage mich manchmal echt ob ich eine andere Sprache spreche als ihr.

    Nö, du weißt nur noch nicht, daß Worte verschiedenes bedeuten können ;p



  • peterchen schrieb:

    namespace std
    {
      template <class _E> struct char_traits;
      template <class _E> class allocator;
    
      template<class _E, class _Tr = char_traits<_E>, class _A=allocator<_E> > class basic_string;
    
      typedef basic_string<char> string;
    }
    
    void foo(std::string const &);
    // ...
    #include <string>
    void foo(std::string const & s)
    {
        printf(s.c_str());
    }
    

    Ist nicht 100% portabel, aber geht.

    Klar. Wir definieren "geht" einfach so lange hin und her, bis du recht hast.
    Die Tatsache, dass wir hier im *Standard*-C++ Forum sind, vergessen wir und
    auch den C++ Standard lassen wir dabei lieber außer acht (der ist in diesem Punkt so unangenehm eindeutig und behindert unsere Kreativität).
    Du hast recht und ich bin zu doof ein typedef zuschreiben. Ist klar.

    Nö, du weißt nur noch nicht, daß Worte verschiedenes bedeuten können ;p

    Richtig. Das war's. Für einen Moment hatte ich schon fast gedacht, der Fehler würde mal nicht bei mir liegen...



  • Ich hätte nicht gedacht, das meine Frage soviel Posts nachsich trägt *g*
    Auf diesem Wege nochmals ein Dankeschön für alle Hinweise und Ratschläge 😉

    Xecutor



  • Die Tatsache, dass wir hier im *Standard*-C++ Forum sind, vergessen wir

    *gähn*

    Das ist ein Standard, keine Götze. Ich bin extrem pingelig bei der Unterscheidung zwischen "geht" und "Standardkonform". Aber wenn ich ein problem lösen muß, hau ich noch ein #ifdef für den Compiler / die STL-Implementation rein, das #incldue <string> geht in den else-zweeig, ein Kommentar und fertig.

    ich finde es richtig und wichtig, auf mögliche Inkompatibilitäten und. Abweichungen vom Standard hinzuweisen. Scheuklappen werde ich mir deshalb nicht anlegen. Die Frage bezog sich auf Programmierpraxis, und nichts, was von Standard geregelt ist.

    Du hast recht und ich bin zu doof ein typedef zuschreiben

    Hab ich nicht gedacht. Entschuldige, wenn es so rübergekommen ist.

    Was mein letzter Satz bedeuten sollte: Das bei "self contained header" alle nicken, und trotzdem jeder was leicht unterschiedliches darunter versteht, ist eine wichtige Erfahrung der Teamarbeit. (z.B. ist für mich ein Header, der eine Klasse definiert, zu deren Instanziertung ich aber noch einen anderen Header brauche, nicht "self-contained", nach der von dir bevorzugten Definition schon)

    War wahrscheinlich ein perfektes Beispiel für sich selbst 🙄

    @Xecutor: soory, aber ich scheine in diesem Forum immer anzuecken.



  • @peterchen
    Wenn die Lösung eines Problems einen wirklich dazu zwingt vom Standard abzuweichen, dann nur zu. Nur stelle ich mir in solchen Situationen immer zwei Fragen:
    a) Gibt es eine Standardlösung?
    b) und falls ja, wie teuer ist die Standard-Lösung im Vergleich?

    Die erste Frage schützt davor aus Unwissenheit auf unnütze unportable Hacks zu vertrauen. Die zweite Frage schützt vor ineffizienter Standard-Sturrheit.

    Desweiteren prüfe ich immer gerne, ab es sich überhaupt lohnt ein Problem zu lösen bzw. ob ich überhaupt ein Problem habe (wie mit dem Messen vor dem Optimieren).
    Welches Problem löst die illegale Vorausdeklaration von std::string?
    Imo löst das kein Problem sondern schafft ein neues (ich muss für jeden Compiler einen anderen Hack schreiben).

    Das bei "self contained header" alle nicken, und trotzdem jeder was leicht unterschiedliches darunter versteht

    Ok. Das mag sein. Allerdings würde mich das doch etwas wundern.
    Da ich davon ausging, dass einige hier sogar gar nicht wissen, was "self contained header" sind, habe ich extra einen Link und einen Hinweis auf John Lakos "Large-Scale C++ Software Design" gepostet. Damit dachte ich eigentlich, jedem die Chance gegeben zu haben, nachzulesen wovon ich spreche.

    z.B. ist für mich ein Header, der eine Klasse definiert, zu deren Instanziertung ich aber noch einen anderen Header brauche, nicht "self-contained", nach der von dir bevorzugten Definition schon

    Das macht dein Posting mit der Erklärung der "gegenteiligen Regel" als Antwort auf meine Frage aber nicht richtiger.
    Bashar sprach von Headern, die inkludiert in eine cpp-Datei nicht kompilerbar sind und erst kompilierbar werden, nachdem in der cpp-Datei *vor* dem Header weitere Header inkludiert werden (Header die nicht als eigenständige Einheit betrachtet werden können). Das jemand *diese* Vorgehensweise empfehlen könnte war mir neu. Deshalb meine Frage.
    Dein Beispiel handelte von ganz anderen Headern. Nämlich von solchen mit minimalen includes, also solchen die inkludiert in eine cpp-Datei gerade noch kompilierbar sind (-> solche Header nennt man für gewöhnlich self-contained).
    Bashar sprach von Headern mit zu wenigen physikalischen Abhängigkeiten. Nicht von welchen mit zuvielen.



  • @HumeSikkins:

    1h build-Zeit ist ein Problem, wenn du täglich baust.
    Der "Hack" ist m.E. insofern OK, als man jederzeit auf eine Standardlösung (#include <string>) zurückfallen kann, wenn es nicht geht. Projekte dieser Größenordnung haben sowieso meist ein fest abgesteckten Portabilitätsbereich.

    Für die "gegenteilige" (also keine forward-decl und kein include) kann ich mir nur den gleichen Grund vorstellen: compile-zeiten zu minimieren. Ein glück das mein compiler #pragma once kennt 🙄



  • peterchen schrieb:

    Für die "gegenteilige" (also keine forward-decl und kein include) kann ich mir nur den gleichen Grund vorstellen: compile-zeiten zu minimieren. Ein glück das mein compiler #pragma once kennt 🙄

    Oder du verwendest einfach redundante Include-Guards. Das erfordert zwar etwas Disziplin (alle Header müssen die selbe Include-Guards-Namensgebung verwenden, Standard-Includes müssen mit einem zusätzlichen define abgeschlossen werden) und deshalb mehr Aufwand, hat aber den Vorteil, dass es nicht auf einen bestimmten Compiler beschränkt ist.

    Nur damit hier nicht wieder Mißverständnisse aufkommmen:
    Redundante Include-Guards nennt man solche, die eine #include-Anweisung umschließen:

    // a.h
    #ifndef A_H_INCLUDED // normale Guards
    #define A_H_INCLUDED 
    ...
    #endif 
    
    // b.h
    #ifndef B_H_INCLUDED // normale Guards
    #define B_H_INCLUDED 
    
    #ifndef A_H_INCLUDED  // redundanter Guard
    #include "a.h"
    #endif
    #ifndef CMATH_INCLUDED
    #include <cmath>
    #define CMATH_INCLUDED  // zusätzliches define für Standardheader
    #endif
    

    Normale Include-Guards schützen nur vor Mehrfach-Includes. Der Präprozessor muss aber trotzdem immer die ganze Datei lesen (auf der Suche nach dem schließenden #endif). Verwendet man Redundante Include-Guards entfällt dieser Aufwand.


Anmelden zum Antworten