Klassenteile in versch. Dateien aufspalten



  • Sry für den aussagelosen Namen - wenn jmd nen besseren Name hat, kann ichs auch ändern ^^

    Die Frage an sich ist relativ kurz:
    Haltet ihr so etwas für sinnvoll (nicht, die liste neu zu schreiben - das ist relativ sinnfrei, aber ok - es geht nur um die iteratoren, ob man sie in ne extra datei schreiben sollte und dann ausnutzen sollte, dass include nur ne textuelle ersetzung durchführt oder ob ihr es in die gleiche datei schreiben würdet):

    /*list.h*/
    
    template <class T>
    class list :	public list_base <T> //typedefs
    {
    //iterators:
    	struct node;
    	#include "list_iterators.h"
    public:
    	typedef Titerator iterator;
    	typedef Tconst_iterator const_iterator;
    /*...*/
    };
    
    /*list_iterators.h*/
    struct Titerator :	my::typedefs::iterator_base<value_type, std::bidirectional_iterator_tag, size_type> //...
    {
    	friend list;
    	friend struct Tconst_iterator;
    public:
    /*...*/
    };
    
    //struct Tconst_iterator : /*...*/ { /*...*/ };
    

    Das gleiche hab ich dann noch mal für die std::swap-Spezialisierung (die dann aber logischerweiße ganz am Ende erst included wird und nicht in der class-definition) gemacht - bin mir nur gerade nicht mehr sicher, ob es sinnvoll war oder nicht - und falls ja, wie weit man das ganze treiben sollte...

    bb



  • Naja, wenn du den List-Iterator als eigenständige Klasse bestehen lassen kannst (d.h. auch ohne die list), dann kann das durchaus sinnvoll sein, ihn auch in einem eigenen Sourcecode zu haben. Ich bin zur zeit dabei, einen std::vector zu implementieren (genauso sinnfrei 🤡) und lagere die verschiedenen verwendeten Klassen auch aus: einen array_iterator als Iteratorklasse und eine Klasse mem_chunk für die Verwaltung von unitialisiertem Speicher. Eine weitere kleine Hilfsklasse die nur im vector selber Sinn macht hab ich auch in der Datei für den vector gelassen, sie macht eigenständig keinen Sinn und ist nicht so groß dass es sich lohnen würde sie auszulagern (kann aber noch passieren beim einen oder anderen Refactoring).


  • Administrator

    Da es mich gerade auch interessieren würde, füge ich noch eine Möglichkeit dazu mit angehängter Frage 🙂

    namespace deinNamespace {
      // ...
    
    namespace detail {
      // ...
    
      class ListIterator // ...
    
    } // detail
    
      // ...
    
      class List
      {
      public:
        typedef detail::ListIterator iterator;
      };
    
    } // deinNamespace
    

    Was spricht gegen eine solche Möglichkeit? Man könnte auch die Deklaration von Iterator in ein eigenes File auslagern, halt einfach nicht in der Klasse List behalten. Ich mag diese Subklassen nicht so, sieht einfach hässlich aus 😉

    Grüssli



  • Ich mag die Subklassen auch nicht so - allerdings find ich die auslagerung in namespaces auch net so hübsch - außerdem muss man dann den ganzen template müll wieder beachten:

    namespace detail
    {
      template <typename T>
      struct node_base
      {
        //
      };
    
      template <typename T>
      struct node : node_base<T>
      {
        T val;
      };
    
      template <typename T>
      struct base_iterator
      {
        friend template <typename T> struct list<T>; //oder so in etwa, weiß ich gerad net ausm kopf ^^
        typedef T value_type;
        /* übrige typedefs */;
      };
    
    /*iterator + const_iterator*/
    }
    
    template <typename T>
    class list
    {
    public:
      //typedef auf node, base_node, iterator und const_iterator
    
    /*impl.*/
    };
    

    also irgendwie siehts nich viel besser aus... aber man könnte evtl wenigsten die includes an die richtige stelle setzen - mehr vorteile fallen mir hier aber nich ein ^^
    z.bsp. braucht man ja <algorithm> für std::swap, den man da net includen kann, dann noch <iterator> für iteratoren für std::bidirectional_iterator_tag und hat beide ganz oben in list.h stehen, obwohl sie da net so wirklich hingehören...

    falls du nach den ganzen punkten von oben immernoch meinst, dass es sauberer/besser wäre, dann sag das ma pls, dann mach ich das auch so ^^
    ich könnte aber dort noch nen einfaches struct drum rum machen, damit ich das template zeugs net jedes ma aufs neue schreiben muss - oder was meinst du dazu?

    bb

    PS: @pumuckl: auch studium oder wieso vector implementieren? ^^

    edit: node_base muss doch kein template sein ^^
    würde ja nur so in etwa aussehen:

    struct node_base
    {
    	node_base *next;
    	node_base *prev;
    
    	node_base() {}
    	node_base(node_base* _next, node_base* _prev) : next(_next), prev(_prev) {}
    };
    

  • Administrator

    Entweder bin ich grad etwas müde oder ... ka, mein Hirn hat womöglich geschlossen, aber ich verstehe irgendwie eine Punkte nicht, welche du aufführst.

    unskilled schrieb:

    außerdem muss man dann den ganzen template müll wieder beachten

    Wo ist hier der Unterschied zu dienem System? Was für einen Müll gibt es da speziell zu beachten, was es sonst nicht gäbe?

    unskilled schrieb:

    /* ... */ friend /* ... */
    

    Wieso brauchst du ein friend in iterator_base ?

    unskilled schrieb:

    also irgendwie siehts nich viel besser aus...

    Optimal definitiv nicht, aber vielleicht doch etwas besser 🙂

    unskilled schrieb:

    ... aber man könnte evtl wenigsten die includes an die richtige stelle setzen - mehr vorteile fallen mir hier aber nich ein ^^

    Ehm, wie bitte? Meinst du sie jetzt oben stehen statt in der Klasse? Also quasi ein #include "ListIteratorsDetail.hpp" oben bei List.hpp?

    unskilled schrieb:

    z.bsp. braucht man ja <algorithm> für std::swap, den man da net includen kann, dann noch <iterator> für iteratoren für std::bidirectional_iterator_tag und hat beide ganz oben in list.h stehen, obwohl sie da net so wirklich hingehören...

    Keine Ahnung, was du damit ausdrücken willst. Wieso gehören sie dort nicht hin? Wieso in list.h? Du könntest sie ja auch auslagern zum File mit den Iteratoren. Was willst du hier aussagen? 😕

    unskilled schrieb:

    falls du nach den ganzen punkten von oben immernoch meinst, dass es sauberer/besser wäre, dann sag das ma pls, dann mach ich das auch so ^^

    Ich frage ja selber :p

    unskilled schrieb:

    ich könnte aber dort noch nen einfaches struct drum rum machen, damit ich das template zeugs net jedes ma aufs neue schreiben muss - oder was meinst du dazu?

    Was? Das Problem ist, dass du immer wieder template<typename T> hinschreiben musst? *ungläubisch schaut, dass jemand dies als Problem sehen kann* 😃

    unskilled schrieb:

    PS: @pumuckl: auch studium oder wieso vector implementieren? ^^

    Nein, er implementiert einen Vektor für seinen dritten Artikel, wo er WIEDER AUF BOOST::OPERATORS EINGEHT, STATT SEIN VERSPRECHEN AUS DEM ERSTEN ARTIKEL ZU HALTEN ... dort hat er nämlich gesagt, er würde im zweiten Artikel etwas über die Operatoren new und new[] sagen.
    ->

    pumuckl schrieb:

    Eine umfassende Besprechung der Speicherverwaltungsoperatoren und ihrer Anwendung ist für den zweiten Teil des Artikels geplant, ...

    Das hätte mich sehr interessiert 🙂

    Naja, vielleicht holt er es ja mit der Speicherverwaltung des Vektors nach. Wenn nicht gibt es haue 😉

    Grüssli



  • Uff hast du viel geschrieben... : D

    Dravere schrieb:

    unskilled schrieb:

    außerdem muss man dann den ganzen template müll wieder beachten

    Wo ist hier der Unterschied zu dienem System? Was für einen Müll gibt es da speziell zu beachten, was es sonst nicht gäbe?

    Naja - sonst schreib ich einmal template und hab meine ganzen typedefs:

    template <class T>
    class list
    {
      struct node : base_node
      {};
      struct iterator : iterator_base
      {};
    //etc.
    };
    

    so muss ich aber jedes einzeln als template sehen und jedes mal wieder typedefs machen (bzw vom typedef-struct ableiten) und muss eben jedes ma nen einzelnes template<class T> davor schreiben...

    Dravere schrieb:

    unskilled schrieb:

    /* ... */ friend /* ... */
    

    Wieso brauchst du ein friend in iterator_base ?

    Weil es nen private CTor gibt, der ein node_base* akzeptiert - der sollte nach außen natürlich nicht zugänglich sein (so sollte das ja auch üblich sein?!):

    struct iterator : /*...*/
    {
    /*...*/
    private:
    	node *data;
    
    	Titerator (node *_data = nullptr) : data(_data) {}
    };
    

    Dravere schrieb:

    unskilled schrieb:

    z.bsp. braucht man ja <algorithm> für std::swap, den man da net includen kann, dann noch <iterator> für iteratoren für std::bidirectional_iterator_tag und hat beide ganz oben in list.h stehen, obwohl sie da net so wirklich hingehören...

    Keine Ahnung, was du damit ausdrücken willst. Wieso gehören sie dort nicht hin? Wieso in list.h? Du könntest sie ja auch auslagern zum File mit den Iteratoren. Was willst du hier aussagen? 😕

    Naja - so kann ich es eben nicht in der list_iterator.h includen, weil ich dort nich nur im scope meines namespaces bin sondern vor allem in der class-definition von list... es ist ja bis jz so:

    class list
    {
     #include "list_iterator.h"
    };
    

    also kann ich iterator nicht in der list_iterator.h includen...
    das mit #include <algorithm> kannst du vergessen - das geht ja wieder, weil man swap ja eh außerhalb des eigenen namespaces spezialisiert...

    Dravere schrieb:

    unskilled schrieb:

    ich könnte aber dort noch nen einfaches struct drum rum machen, damit ich das template zeugs net jedes ma aufs neue schreiben muss - oder was meinst du dazu?

    Was? Das Problem ist, dass du immer wieder template<typename T> hinschreiben musst? *ungläubisch schaut, dass jemand dies als Problem sehen kann* 😃

    Naja - ich muss auch jedes ma wieder alle nötigen typedefs machen bzw von nem struct ableiten, was die beinhaltet - und es wäre eben wahrscheinlich auch nich so super elegant, jedes ma das selbe zu schreiben, also dacht ich an so was:

    namespace detail
    {
      template <typename T>
      struct list_stuff : typedefs::container <T, std::size_t>
      {
        struct node {/*...*/};
    
        struct iterator {/*...*/};
        struct const_iterator {/*...*/};
      };
    }
    

    Und hätte nicht jedes ma wieder bestimmte typedefs zu machen...
    Das könnte ich dann aber wieder nur komplett in eine Datei auslagern und nicht in list_node und list_iterator aufspalten... Aber naja - man könnte es wenigsten list_stuff.h nennen und man hätte es scho relativ gut getrennt - ich glaub, so werd ich es machen - oder was meinst du, ist es so ordentlicher/lesbarer/...?

    Dravere schrieb:

    unskilled schrieb:

    PS: @pumuckl: auch studium oder wieso vector implementieren? ^^

    Nein, er implementiert einen Vektor für seinen dritten Artikel, wo er WIEDER AUF BOOST::OPERATORS EINGEHT, STATT SEIN VERSPRECHEN AUS DEM ERSTEN ARTIKEL ZU HALTEN ... dort hat er nämlich gesagt, er würde im zweiten Artikel etwas über die Operatoren new und new[] sagen.
    ->

    Ach stimmt ja - naja, hatte nich damit gerechnet, dass er die Artikel so schnell hintereinander macht - sehr vorbildlich : >

    Dravere schrieb:

    pumuckl schrieb:

    Eine umfassende Besprechung der Speicherverwaltungsoperatoren und ihrer Anwendung ist für den zweiten Teil des Artikels geplant, ...

    Das hätte mich sehr interessiert 🙂

    Mich auch - aber boost::operators ist eben auch nich gerade langweilig ^^ Gibts eben nen paar Artikel mehr von pumuckl!? 😉

    bb



  • Dravere schrieb:

    Wo ist hier der Unterschied zu dienem System? Was für einen Müll gibt es da speziell zu beachten, was es sonst nicht gäbe?

    Subklassen eines Klassentemplates sind implizit wieder Klassentemplates. Lagert man sie aus muss man sie zu expliziten Klassentemplates machen. Subklassen haben außerdem Zugriff auf die Interna der umgebenden Klasse, das kann nötig sein. Lagert man die Subklasse aus kann bei entsprechend starker Abhängigkeit ein friend vonnöten sein. IMO ist genau das das Entscheidungskriterium Subklasse vs. eigenständige Klasse: Ist es Kontextfrei, dann machs eigenständig. Ists eine starke Abhängigkeit von der umgebenden Klasse, lass es da.

    unskilled schrieb:

    PS: @pumuckl: auch studium oder wieso vector implementieren? ^^

    Nein, mein Studium hab ich gottseidank letztes Jahr abgeschlossen (leider wars nichtmal Informatik). Ich hatte angefangen für meine Iteratorklasse (kommt im besagten dritten Artikel) eine Grundlage zu schaffen worauf sie iterieren kann - und erweitere es just for fun zu einem "echten" std::vector, zumindest vom Interface her.

    Meine Anordnung der Klassen bisher:

    //array_iterator.hpp /////////////////////////
    namespace pumu {
      namespace util {
    
    template <class T>
    class array_iterator //...
    }} //end namespace pumu::util
    
    //memchunk.hpp /////////////////////////
    namespace pumu {
      namespace memory {
    
    template <class T, class Allocator>
    struct MemChunk //...
    }} //end namespace pumu::memory
    
    //vector.hpp /////////////////////////
    #include "pumu/memory/memchunk.hpp"
    #include "pumu/util/array_iterator.hpp"
    
    namespace pumu {
      namespace container {
    
    template <class T, class Allocator>
    class vector
    {
      /*...*/
    public:
      typedef typename pumu::util::array_iterator<T> iterator;
      /*...*/
    private:
      MemChunk<T, Allocator> mem;
    
      struct ConstructGuard; //private Klasse, braucht Zugriff auf private Methoden.
    };
    

    Die Iteratorklasse ist von vornherein ls eigenständige Klasse konzipiert gewesen. Die MemChunk-Klasse war zuerst überhaupt nicht vorhanden, dann habe ich bei einem ersten Refactoring die Speicherverwaltung in eine private Subklasse ausgelagert, die aber eigenständig genug war, so dass sie als eigene Klasse ausgelagert werden konnte. Was ich hier schon skizziert habe ist der nächste Schritt: das Delegieren der Speicherbeschaffung an eine Allokatorklasse statt an operator new direkt - die Änderung erfordert keinerlei Änderungen an der vector-Klasse sondern nur an memchunk selber, ein weiteres Indiz dafür dass Memchunk als eingenständige Klasse bestehen sollte.

    Nein, er implementiert einen Vektor für seinen dritten Artikel, wo er WIEDER AUF BOOST::OPERATORS EINGEHT, STATT SEIN VERSPRECHEN AUS DEM ERSTEN ARTIKEL ZU HALTEN ... dort hat er nämlich gesagt, er würde im zweiten Artikel etwas über die Operatoren new und new[] sagen.
    ->

    pumuckl schrieb:

    Eine umfassende Besprechung der Speicherverwaltungsoperatoren und ihrer Anwendung ist für den zweiten Teil des Artikels geplant, ...

    Das hätte mich sehr interessiert 🙂

    Naja, vielleicht holt er es ja mit der Speicherverwaltung des Vektors nach. Wenn nicht gibt es haue 😉

    Grüssli

    "Ist geplant" heißt nicht versprochen 😛
    die Planung hat sich seit Januar etwas verschoben, das stimmt. Und ja, Teil 3 geht wieder auf boost::operators ein, das war nciht geplant, aber ich musste den Artikel splitten weil er sonst zu lang geworden wäre. Operator new/delete sind auf jeden Fall noch geplant, in welchem Umfang und was alles behandelt wird weiß ich noch nicht. Die Speicherverwaltung des vector gibt da nicht viel her. Der vector selber macht nur placement-new und explizite Dtor-Aufrufe, die Speicherbeschaffung wird über den Memchunk an den Allocator delegiert.



  • @pumu':
    Auf deine ursprüngliche Frage:
    Ich würde *niemals* ein Header-File machen, welches *in* einer Klasse inkludiert werden muss.

    #include hat gefälligst immer nur auf "File-Scope" zu stehen, also ausserhalb jeder Namespaces, Klassen und Funktionen.

    Die einzige Ausnahme hier wären Headers die für Preprozessor-Magick verwendet werden. Wobei ich auch kein besonderer Freund von Preprozessor-Magick bin.

    Wenn du deine Klasse auf mehrere Files aufteilen willst, dann ...
    a) verwende Klassen in Detail-Namespaces anstelle von nested Classes
    b) splitte Deklaration und Definition auf (das geht ja schliesslich auch bei Templates) - dann kannst du ganz leicht mehrere Files für die Definition verwenden wenn du unbedingt willst. Am ende des ".hpp" Files inkludierst du dann einfach alle ".ipp"/".imp" Files (=die Files mit den Definitionen drinnen).

    Oft ist IMO (a) die bessere Möglichkeit, da man dadurch die IMO hässliche Syntax für von der Deklaration getrennte Definitionen umgeht. Und oft kann man einige dieser Klassen dann für mehr als nur eine "äussere" Klasse einsetzen.

    Oft verbietet man den Zugriff auf diese Detail Klassen dann auch nicht (wäre meist ziemlich viel "friend" Tippaufwand). Ist aber IMO kein Problem. Wer in seinem Code Fremde Detail-Klassen verwendet, ist des selberen schuld wenns mit der nächsten Version des Fremd-Codes nichtmehr geht.



  • War meine Frage, aber danke ^^

    und ich werds trennen (in nen detail namespace)

    ob ichs noch in deklaration und *.inl / .impl (.ipp und nur *.imp hab ich noch nie gesehen - aber das hat ja nix zu sagen ^^) trennen, weiß ich noch nicht genau - also selbst verständlich nur den code, der jz im "main"-file stehen geblieben ist - also der zu list... eigtl bin ich da bei templates nich so der fan von - bsp.:

    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;
    }
    

    vs

    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;
    }
    

    bb



  • War meine Frage, aber danke ^^

    lol
    Hast du auch wieder Recht... 😃

    Und genau das meinte ich mit "hässlicher Syntax" 🙂

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



  • Da bin ich mir beim vector noch unschlüssig. Das Ding hat ja eine ganze Menge Methoden, die Datei kommt am Ende sicherlich auf 700-1000 Zeilen. Bei so einer Menge finde ich eine möglichst kurze Klassendefinition (allein mit Methodendeklarationen und 2 Membervariablen schon 87 Zeilen) schon lang genug.

    Ich bin am überlegen ob man die hässliche Syntax durch ein oder zwei Makros etwas aufpolieren könnte:

    //alte Version:
    template <class T>
    typename vector<T>::iterator vector<T>::begin()
    {
      return iterator(ptrbegin());
    }
    
    template <class T>
    void vector<T>::resize(typename vector<T>::size_type sz, T c = T())
    {
      if (sz <= size()) return;
      reserve(sz);
      constructBackN(sz-size(), c);
    }
    
    //neue Version:
    #define VEC_METHOD(type) \
    template <class T> \
    type vector<T>::
    
    #define INNER_TYPE_METHOD(type) \
    template <class T> \
    typename vector<T>::##type vector<T>::
    
    #define DEP_T(type) \
    typename vector<T>::#type
    
    INNER_TYPE_METHOD(iterator) begin()
    {
      return iterator(ptrbegin());
    }
    
    VEC_METHOD(void) resize(DEP_T(size_type) sz, T c = T())
    {
      if (sz <= size()) return;
      reserve(sz);
      constructBackN(sz-size(), c);
    }
    

    so ganz glücklich bin ihc damit aber nicht...



  • @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.



  • 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


Anmelden zum Antworten