Klassenteile in versch. Dateien aufspalten



  • hustbaer schrieb:

    @pumuckl:
    Also ich hasse solche Makros. Inbrünstig.
    Weil sie die Zeit die man braucht um den Code zu lesen (wenn man ihn noch nicht kennt) vervielfachen.

    Wenns zu viele sind durchaus. Wie schon gesagt, glücklich war ich damit nicht.
    Mit einer 800-Zeilen Klasse wäre ich aber noch weniger glücklich, und die Syntax der externen definition ist halt hässlich....



  • hustbaer schrieb:

    Deswegen bin ich in letzter Zeit auch dazu übergegangen, Klassen-Templates fast ausschliesslich implizit inline zu schreiben.

    Ich auch - aber wie pumuckl schon sagt... es ist nicht gerade übersichtlicher dadurch x)
    das makro find ich aber auch hässlich... es bringt dir eine zeile pro funktion - also vll 40 zeichen pro funktion - ich würd sie lieber mitschreiben, als mich dann durch makros wühlen zu müssen....

    bb


  • Administrator

    Darf ich da kurz nochmals nachfragen? Ihr findet sowas hässlich?

    //////////////////////////////////////////////////////////////////////
    // NamedObject
    
    template<typename T>
    class NamedObject
    {
      // Typedefs //
    public:
      typedef T Type;
    
      // Attributes //
    public:
      Type m_object;
      std::string m_name;
    
      // Constructors //
    public:
      NamedObject(std::string const& name, Type const& object);
    
      // Methods //
    public:
      Type const& get_object() const;
      std::string const& get_name() const;
    };
    
    //////////////////////////////////////////////////////////////////////
    // Templates implementation
    
    //////////////////////////////////////////////////////////////////////
    // NamedObject
    
    /********************************************************************/
    /* Constructors                                                     */
    /********************************************************************/
    
    template<typename T>
    NamedObject<T>::NamedObject(std::string const& name, Type const& object)
      : m_name(name)
      , m_object(object)
    {
    }
    
    /********************************************************************/
    /* Methods                                                          */
    /********************************************************************/
    
    template<typename T>
    T const& NamedObject<T>::get_object() const
    {
      return m_object;
    }
    
    /********************************************************************/
    
    template<typename T>
    std::string const& NamedObject<T>::get_name() const
    {
      return m_name;
    }
    

    Was genau mögt ihr daran nicht? Ich mag nämlich so eine Aufteilung. Gut wir reden hier jetzt über Geschmack, aber es würde mich interessieren, was genau ihr daran nicht mögt 😉

    Grüssli



  • Zwei Möglichkeiten: entweder die Klasse ist tatsächlich so kurz, dann sind die diversen Kommentare überflüssig - die Klasse wäre dann übersichtlich genug. 😛

    Oder die Klasse ist sehr viel länger, dann hätte man nicht vor drei sondern vor zig Methoden ein template<typename T> NamedObject<T>:: usw. stehen - und die ständige Wiederholung ist halt auch nicht sehr schön.
    Vector hat mit Überladungen mal schlappe 40 Methoden, viele haben davon interne typedefs als Rückgabetyp und/oder Parameter, so dass da jedesmal noch ein typename vector<T, Allocator>::iterator oder ähnliches dazu muss - dann kommen noch die freien Vergleichsfunktionen dazu. Überschlagen sind das 60-80 mal vector<T,Allocator>:: , 40x template<class T, class Allocator> , 20-40x typename - nervig!



  • ich hatte doch oben scho nen Beispiel gemacht...

    template <typename T> 
    void list<T>::_erase(typename list<T>::node *to_del, typename list<T>::node_base *before, typename list<T>::node_base *after) 
    { 
        assert(to_del && before && after); 
        assert( (to_del != &m.anchor_begin) && (to_del != &m.anchor_end) ); 
    
        before->next = after; 
        after->prev = before; 
        delete to_del; 
    }
    

    ist einfach hässlich -.-

    vor allem, wenn ichs auch so machen könnte:

    void _erase(node *to_del, node_base *before, node_base *after)
    {
        assert(to_del && before && after);
        assert( (to_del != &m.anchor_begin) && (to_del != &m.anchor_end) );
    
        before->next = after;
        after->prev = before;
        delete to_del;
    }
    

    meinste nicht auch? der nachteil liegt natürlich auch auf der hand:
    die klasse ist voll mit definitionen und so sind die deklarationen schwerer zu finden...
    naja - so, wie es jz aussieht, werd ich es zwar trennen, aber so ganz glücklich damit bin ich eben auch noch nicht... -.-

    Man sieht ja schon hier:
    3x typename
    4x list<T>::
    1x template <typename T>

    und das ist nur eine einzige Funktion... -.-

    bb

    PS: Hab gerad pumuckls Post gesehen(Vorschau) - genau das ist es eben - nervig und unschön - bei mir wäre es eben auch so in etwa...


  • Administrator

    pumuckl schrieb:

    Zwei Möglichkeiten: entweder die Klasse ist tatsächlich so kurz, dann sind die diversen Kommentare überflüssig - die Klasse wäre dann übersichtlich genug. 😛

    Ich bleibe bei der Darstellung der Klassen einheitlich, deshalb sind die Kommentare bei mir immer vorhanden, egal ob es eine grosse oder kleine Klasse ist 😉

    pumuckl schrieb:

    Überschlagen sind das 60-80 mal vector<T,Allocator>:: , 40x template<class T, class Allocator> , 20-40x typename - nervig!

    Ach, es geht dir hier nur ums schreiben? Da sehe ich keine Probleme, da man sich hier ganz einfache Abhilfe über Copy&Paste schaffen kann, weil es eben immer das gleiche ist. Sowas hat man in ca. einer Minute erledigt, auch bei 60-80 Methoden 😉
    1. Man kann die Deklaration der Funktionen kopieren und für die Definition verwenden.
    2. Vor die Funktionen muss jeweils zuerst ein template<...> und in einem zweiten Durchlauf ein Class<...>:: . Maus & <Ctrl> + <V> -> zack, zack, zack 😉
    3. Zum Teil muss noch ein typename Class<...>:: vor den Rückgabewert. Wieder Maus & <Ctrl> + <V> -> zack, zack, zack 😉
    4. Ich muss noch meine Kommentare einfügen. Die grobe Unterteilung der Bereiche ist einfach, da habe ich zusätzlich sogar noch Snippets.
    Für die Trennung der Methoden, ist es auch immer die gleiche Linie und noch eine neue Linie. Falls ein zusätzliche Abstand noch nicht vorhanden ist, füge ich per <Enter> noch eine ein. Wieder Copy&Paste, dass geht wieder zack, zack, zack 😉
    5. Alle Semikolons durch eine neue Zeile und darunter '{' neue Zeile '}' ersetzen lassen. Das ist auch schnell erledigt.
    (Edit: 4. und 5. mache ich oft sogar zusammen und füge dann nur noch fehlende neue Zeilen per <Enter> ein ;))

    @unskilled,
    Die Funktionssignatur kannst du kürzen:

    template<typename T> 
    void list<T>::_erase(node *to_del, node_base *before, node_base *after)
    

    Grüssli



  • Oo

    Wusst ich noch gar nicht - auf die Idee wär ich auch nich gekommen ^^

    Danke : >


  • Administrator

    unskilled schrieb:

    Wusst ich noch gar nicht - auf die Idee wär ich auch nich gekommen ^^

    Hmmm, vielleicht noch zur Erklärung, wieso das hier geht:
    Sobald du mit list<T>:: gesagt hast, wo die Funktion liegt, ist der entsprechende Scope bekannt. Somit wird für die Suche nach den Typen auch der Scope der Klasse list<T> herangezogen.
    Das ist auch der Grund, wieso es nicht für den Rückgabetypen geht. Dort ist der Scope der Funktion noch nicht bekannt.
    Und das ist einer der Vorteile von C++0x, wo man den Rückgabetypen hinter die Funktionssignatur schreiben können wird, da dort wieder der Scope bekannt ist. In C++0x wird man somit nicht mal mehr beim Rückgabetypen typename list<T>:: hinschreiben müssen 😉

    Grüssli



  • Danke - fie Erklärung an sich hatte ich jz auch scho ergoogelt ^^

    Das Feature des neuen Standards kannte ich noch gar nich - aber hab mich allgemein noch nich so sehr damit beschäfitgt - gibt auch so noch genug, was ich nich weiß 😉
    Aber danke noch mal : >

    bb



  • template <class GreenType, class ShadyBlueType>
    class FooBarBazQux
    {
    public:
    	class State {};
    	class LaliDo {};
    
    	// lieber so ...
    	static boost::shared_ptr<State> FiFaFunction(GreenType green, LaliDo lali);
    };
    
    // ... oder so?
    template <class GreenType, class ShadyBlueType>
    boost::shared_ptr<typename FooBarBazQux<GreenType, ShadyBlueType>::State> FooBarBazQux<GreenType, ShadyBlueType>::FiFaFunction(
            GreenType green,
            typename FooBarBazQux<GreenType, ShadyBlueType>::LaliDo lali)
    {
    }
    

    Ich finde da doch eher die erste Variante "besser" 😉



  • Dravere schrieb:

    Ach, es geht dir hier nur ums schreiben?

    Nein, es geht NIE ums schreiben, sondern immer ums lesen. Und wenn der Code vollgemüllt ist mit template<...> , Class<...> und typename Class<...>:: dann machts das Lesen deutlich schwerer.
    Ich denke dass die implizit-inline Version dann doch besser lesbar ist - und da man in den meisten IDEs inzwischen soetwas wie ein "collapse all" hat, ists auch nicht schwer, die ganzen Definitionen auf die Funktionssignaturen zu reduzieren.


  • Administrator

    @pumuckl,
    Dann könntest du aber das gleiche über die Trennung sagen, wenn es sich nicht um Templates handelt. Man hat auch überall ein Class:: davor auch vor Rückgabetypen, welche aus der Klasse stammen. Das ganze wiederholt sich auch die gane Zeit und wenn man alles inline machen würde, könnte man mit der IDE durch "collapse all" auch ohne Probleme die reine Deklaration sehen. 😉

    Wenn du allgemein der Meinung bist, dass die Trennung unübersichtlich ist, dann versteh ich es zwar nicht, bzw. bin anderer Meinung, kann es aber unter Geschmacksache versorgen.
    Wenn du nur Trennung bei Templates als Problem siehst, dann wird in meinem Gehirn eine std::logic_error Exception geworfen 🙂

    @hustbaer,
    Du hast unteranderem den gleichen Fehler gemacht, wie unskilled ihn bereits getan hat 😉
    Zudem kann man das durchaus auch noch ein wenig besser strukturieren.

    template <class GreenType, class ShadyBlueType>
    boost::shared_ptr
    <
      typename FooBarBazQux
      <
        GreenType,
        ShadyBlueType
      >::State
    >
    FooBarBazQux<GreenType, ShadyBlueType>::FiFaFunction(GreenType green, LaliDo lali)
    {
    }
    

    Und mit dem neuen Standard wäre es wohl so, oder? (kenne mich mit der Syntax noch nicht so genau aus:

    template <class GreenType, class ShadyBlueType>
    auto FooBarBazQux<GreenType, ShadyBlueType>::FiFaFunction(GreenType green, LaliDo lali)
      -> boost::shared_ptr<State>
    {
    }
    

    Und ja, ich ziehe sowas vor. Vor allem sind solche komplexe Ausdrücke, wie du einen hier präsentierst, eher selten anzutreffen. Und wenn sie vermehrt anzutreffen sind, dann kann man sie meistens durch ein simples typedef irgendwo oder einer kleinen Hilfstruktur wesentlich vereinfachen.

    Grüssli


Anmelden zum Antworten