"Container" für beliebig viele Variablen eines beliebigen Types implementieren



  • Das hat mir schonmal weitergeholfen 🙂

    Ich habe jetzt eine Type-Variable in jedem Node, in der beim Konstruktoraufruf der Typ der Content-Variable gespeichert wird.
    Außerdem natürlich eine getType-Funktion.
    Dadurch kann ich alle Nodes gut in einer Map (map<type_info*,vector<TreeNode*>> speichern und kann damit vorläufig alle Managment-Funktionen realisieren.
    An sich brauche ich keinen cast mit einem zur Laufzeit ermittelten Typ (was ja auch nicht möglich ist, wie ich gelernt habe :)).
    Grundsätzlich kennt man ja die Datentypen der gespeicherten Variablen.

    Ich werde mich mal an die implementierung der Container-Klasse machen, die Node-Klasse sollte halbwegs fertig ein.

    Ach ja, fällt jemandem vielleicht ein guter Name für den Container ein? Bin grad furchtbar unkreativ 😃

    VG
    Cherup



  • So,

    hab die Container-Klasse grundlegend implementiert (ich hoffe halbwegs richtig ;)), werd aber noch weiter daran feilen... Der Compiler spuckt keine Fehler aus.

    Jetzt habe ich zum testen mal einen neuen Container erstellt und wollte ihn mit ints befüllen. Das Ergebnis war die Fehlermeldung
    "error: undefined reference to `void TreeNodeBase::insert<int>(int)'"

    Ich sehe aber keinen Fehler bei der Implementierung (wahrscheinlich irgendwas blödes... ).

    Ich hoffe ihr könnt mir weiterhelfen.

    Der (reduzierte) Code der Header-Datei:

    class TreeNodeBase{
    private:
        class TreeNode;
    
    //structors
    public:
        TreeNodeBase();
         ~TreeNodeBase();
    
    //functions
    public:
        template<typename T> void insert(T value);
        template<typename T1, typename T2> void insert(T1 value, T2 description);
    
    //Classes
    private:
        class TreeNode{
        (...)
        };
    
    //Storage
    private:
        std::map<std::type_info*, std::vector<TreeNode*>> myNodes;
    };
    

    Die dazu gehörenden Funktionen der .cpp:

    template<typename T> void TreeNodeBase::insert(T value)
    {
        myNodes[const_cast<std::type_info*>(&typeid(T))].push_back(new TreeNode(value));
    }
    template<typename T1, typename T2> void TreeNodeBase::insert(T1 value, T2 description)
    {
        myNodes[const_cast<std::type_info*>(&typeid(T1))].push_back(new TreeNode(value,description));
    }
    

    und der Aufruf der insert-Funktion:

    TreeNodeBase* myContainer = new TreeNodeBase();
    for(int i=0; i<10; i++) myContainer->insert(i); //Fehler
    delete myContainer;
    

    Kann man eigentlich das

    const_cast<std::type_info*>(&typeid(T))
    

    mit einem Alias versehen? Es ist aufwändig zu schreiben und von der Übersicht will ich gar nicht sprechen...

    Wenn das soweit läuft würde ich gern mal eure Meinung zum gesamten Code lesen, wahrscheinlich lässt sich an 1000 Ecken noch was verbessern oder Mist beseitigen.

    Viele Grüße und schönen Feiertag
    Cherup



  • Implementierungen von Funktionstemplates müssen in die .h-Datei.



  • Danke, läuft.

    Dann werd ich mal durchtesten und evtl noch n bissel was weiterentwickeln.

    VG
    Cherup


  • Mod

    Cherup schrieb:

    Kann man eigentlich das

    const_cast<std::type_info*>(&typeid(T))
    

    mit einem Alias versehen?

    Wieso nicht gleich const std::type_info* als map-key? Abgesehen davon ist das sowieso falsch, weil verschiedene typid-Ausdrücke, die sich auf den gleichen Typ bezieht, nicht zwingend das gleiche Objekt liefern.
    Für solche Zwecke gibt es std::type_index.



  • Vielen Danke für den Hinweis, das hat eine Menge Probleme gelöst, vor allem bei den Iterator-Funktionen 🙂

    Ich wußte nicht, das typeid für gleiche Datentypen nicht immer den gleichen Wert liefern. Und ich kannte type_index einfach nicht 🙂

    Viele Grüße
    Cherup



  • Ok, eine vielleicht noch etwas dumme Frage zu type_index:
    Ich möchte den Datentyp des gespeicherten Wertes speichern um sie für Vergleiche zu nutzen. Eine direkte Abfrage ist nicht möglich, weil ich dafür den Typ kennen muss.
    Offenbar kann zumindest ich keine Variablen vom Typ type_index deklarieren.
    Da type_info ja ungeeignet ist, ist die Frage:

    Wie speichere ich sinnvoll den Datentyp?

    Mein Lösungsansatz wäre:
    Ich hole mir den Hash-Code von type_index und speichere ihn (Typ ist size_t).

    Ist das sinnvoll oder gibt es eine bessere Lösung?

    Ich habe auch noch ein "kleines" Problem mit den vector-Iteratoren, aber dazu komme ich später.

    Viele Grüße
    Cherup



  • type_index habe ich ja zusammen mit type_info erwähnt.

    Für so einen Vergleich ist type_info schon ok.



  • hmm, das type_index hab ich dann wohl mit type_info verwechselt 🙄
    Wie auch immer, das läuft jetzt.

    Daher komm ich jetzt zu den Iteratoren 😃 Die Laufen zwar an sich auch, ich habe aber eine Unannehmlichkeit mit den Node-Iteratoren.

    Kurze Implementierungserklärung:
    Ich stelle 4 Iteratoren bereit, einen um über die map zu iterieren ("list_iterator") und einen für den vector in einem map pair ("node_iterator"), jeweils als const und normale Variante. Für jeden Iterator habe ich eine begin() und end() Funktion.
    Die begin() und end() Funktionen der node_iteratoren sind jeweils 4 mal überladen und übernehmen entweder einen template-typ an, eine type_info oder einen const_list_iterator/list_iterator. Ich muss ja wissen, über welchen vector ich iterieren will.

    Wenn ich einen node-iterator bekomme, muss ich immer mit (*it_node)->TuWas() bzw (*it_node)->TuWas<T>() darauf zugreifen, das nervt etwas und erschwert zumindest für mich das Code-Verständnis.

    Ich gebe mal etwas Beispiel-Code:

    //Deklaration
    class TreeNodeBase{
    public:
        typedef std::vector<TreeNode*>::iterator node_iterator;
        typedef std::vector<TreeNode*>::const_iterator const_node_iterator;
        typedef std::map<std::type_index, std::vector<TreeNode*>>::iterator list_iterator;
        typedef std::map<std::type_index, std::vector<TreeNode*>>::const_iterator  const_list_iterator;
    
        template<typename T> node_iterator begin();
        node_iterator begin(std::type_index type);
    
        const_node_iterator begin(std::type_index type) const;
    
        template<typename T> node_iterator end();
        node_iterator *end(std::type_index type);
    
        list_iterator begin();
    };
    
    //Definition
    template<typename T> TreeNodeBase::node_iterator TreeNodeBase::begin(){ return myNodes[typeid(T)].begin(); }
    TreeNodeBase::node_iterator TreeNodeBase::begin(std::type_index type){ return myNodes[type].begin(); }
    
    TreeNodeBase::const_node_iterator TreeNodeBase::begin(std::type_index type) const{ return begin(type); }
    
    template<typename T> TreeNodeBase::node_iterator TreeNodeBase::end(){ return myNodes[typeid(T)].end(); }
    TreeNodeBase::node_iterator TreeNodeBase::end(std::type_index type){ return myNodes[type].end(); }
    
    TreeNodeBase::list_iterator TreeNodeBase::begin(){ return myNodes.begin(); }
    
    //Iterieren durch <int>-Nodes:
    vector<int> myints; //Bloß zum speichern, myContainer ist eine Instanz des Containers
    for(auto it=myContainer->begin(typeid(int)); it!=myContainer->end<int>(); ++it){
        myints.push_back((*it)->getValue<int>()); //Hier ist das (*it)
    }
    

    Ich bin sicher, dass man den iterator irgendwie besser zurückgeben kann, aber wie?
    Und ist diese Art der Implementierung gut/sinnvoll?

    Viele Grüße
    Cherup



  • Naja, du reichst die internen Iteratoren von vector usw. raus. Du kannst die ja in eigene Iteratorobjekte kapseln, die du dann nicht referenzieren müsstest.
    Ich hätt mir von der API her evtl. sowas wie adobe forest vorgestellt:

    http://stlab.adobe.com/group__asl__tutorials__forest.html



  • Mechanics schrieb:

    Ich hätt mir von der API her evtl. sowas wie adobe forest vorgestellt:

    Das liest sich ja fast etwas enttäuscht 😃
    Kommt aber noch, etwas in der Art ist mein Ziel. Ich finde die map<type_index, vector<>>-Struktur noch etwas einfacher/übersichtlicher. Wenn die läuft möchte ich den Container dahingehend erweitern, dass man im Konstruktor über einen Parameter auch eine Baumstruktur wählen kann.
    Wie genau ich die Nodes dann anfüge muss ich mir noch überlegen. Auf jedenfall wird man einen neuen Node auch explizit an einen anderen anhängen können.
    Aber das ist noch Zukunftsmusik, jetzt möchte ich erstmal die Iteratoren gut benutzbar machen.

    Mechanics schrieb:

    Du kannst die ja in eigene Iteratorobjekte kapseln, die du dann nicht referenzieren müsstest.

    Meinst du damit eine simple Klasse mit Operatoren, die nur jeweils die Iteratoroperationen weiterreichen?

    Schönen Restabend
    Cherup



  • Cherup schrieb:

    Meinst du damit eine simple Klasse mit Operatoren, die nur jeweils die Iteratoroperationen weiterreichen?

    Naja, schau dir die Beispiele auf der verlinkten Seiten an. Vielleicht kann man das noch einfacher machen, z.B. den Zuweisungsoperator mit dem jeweiligen Templatetyp überladen usw.



  • So,
    das meiste vom "Standard"-Container sollte fertig sein, die Funktionen sind grundlegend getestet. Ich hänge nur immernoch bei den Iteratoren. Die funktionieren zwar auch, aber ich möchte ja auf die Nodes (bzw. die Node-Funktionen) zugreifen können ohne dereferenzieren zu müssen.
    Ich habe mir mal das verlinkte Beispiel angeschaut, da wird ja für jeden Iterator eine eigene Klasse erstellt. Furchtbar viel Schreibaufwand aber Ok... :).

    Jetzt habe ich aber mal wieder ein Brett vorm Kopf oder besser nen ganzen Stapel.

    Zuerst mal das Design der Iteratorklasse: ich brauche eine Referenz auf den vector in der Speichermap und einen Pointer auf das aktuelle Element, soweit richtig?
    Neben den ganzen begin() und end() Funktionen brauche ich auch die Operator-Funktionen. Ausserdem die Zugriffsfunktionen für die Funktionen der Nodes, in denen ich den Pointer quasi dereferenziere, auch richtig?
    Mal ein schnell dahingeschriebenes Beispiel:

    class node_iterator
    {
    public:
        node_iterator();
        node_iterator(std::vector<TreeNode>* ref) : myVector(ref), myPointer(myVector->begin()) {}
    
    //begin und end-Funktionen (...)
    //Operatoren (...)
    
    //Zugriffsfunktionen
    template<typename T> T getValue(){ return myPointer->getValue<T>(); }
    //(...)
    
    private:
    vector<TreeNode*>* myVector;
    TreeNode* myPointer;
    

    Dann stellt sich mir noch die Frage, wie ich am besten eine Instanz des Node_Iterators erstelle. Wenn ich einfach "return Node_Iterator it(...)"(Konstruktoraufruf) zurückgebe, wird dann eine neue Instanz erstellt oder nur bei new? Bin grad etwas verwirrt 😃
    Und dann noch die Lebensdauer... Ich will den Node_Iterator ja zum einen an andere Funktionen übergeben können und möchte ihn nicht manuel löschen müssen. Wäre es da nicht am besten einen smart-Pointer auf Node_Iterator zurückzugeben?

    Ich weiß, die letzten Fragen sind eigentlich Anfängerfragen, aber irgendwie blick ich da grade nicht durch 😕

    Schönen Tag noch
    Cherup



  • Es fällt mir eh schwer, in deinem Design durchzublicken und es mir immer noch / wieder nicht mehr klar, was du eigentlich willst ^^
    Du wolltest ja ursprünglich sowas wie einen Baum, jetzt hast aber irgendwie den Typ als Map Index... Also musst du vor allem selber mit deinem Code zurechtkommen können 😉

    Iteratoren habe ich vor allem ins Gespräch gebracht, als es noch um einen Baum ging. Ein Baum ist eben eine baumartige Anordnung von Werten. Das geht aber nicht, du brauchst noch zusätzlich Metadaten, deswegen braucht man also Tree Nodes. Die sollten den Benutzer aber nicht interessieren. Der Benutzer sollte möglichst nur mit den eigentlichen Werten arbeiten und nicht die Internas der Baumstruktur manipulieren können. Also holt man sich einen Iterator, z.B. Post Order oder Pre Order, und arbeitet mit dem. Dann kann der Benutzer z.B. sagen, er will an der Iteratorposition ein neues Element einfügen. Alles versteckt, man sieht nie irgendwelche TreeNodes, kann sie nicht anlegen oder löschen. Ich sprech da mehr oder weniger aus Erfahrung, ich hab mal nebenbei so eine Baumstruktur hingerotzt und da sind die TreeNodes Teil der public API, das gefällt mir überhaupt nicht. Das war also die Intention hinter den Iteratoren, obs bei dir noch die beste Lösung ist, weiß ich nicht.

    Es ist ok, wenn der Iterator nur einen Zeiger bekommt und den kapselt. Der muss nicht gültig sein, wenn der Baum nicht mehr gültig ist. Da brauchst auch keine smart pointer und musst dir auch keine Gedanken über Kopien machen.



  • Ok, also passt das Design 🙂
    Was ich will ist relativ simpel:
    einen Container, der beliebig viele Werte eines beliebigen Typs speichern kann, und das um des Lerneffekts willen möglichst komplett selbstgeschrieben.

    Das Design mit der map habe ich jetzt erstmal gewählt um einen allgemeinen Container zu haben (Baum ist ja etwas spezieller) und weil ich mit maps und vectoren mehr Erfahrung habe als mit Bäumen.
    Wenn der "Standard"-Container soweit fertig ist, will ich ihn dahingehend erweitern, dass er auch (aber nicht nur) eine Baum-Struktur annehmen kann.
    So hab ich also zwei in einem: einen "Any-Container" und einen "Any-Tree" in einer Klasse 🙂

    Das Problem mit den Iteratoren bezieht sich im wesentlichen darauf, dass es etwas nervt die vector-iteratoren dereferenzieren zu müssen und das gleichzeit (aus meiner Sicht) zuviel vom Innenleben preisgegeben wird.

    Schönen Abend
    Cherup



  • So,
    ich habe mal mehr oder weniger zum lernen einen Iterator geschrieben und den ganzen Container in train umbenannt (man kann alles mögliche darin durch die Gegend verfrachten und er kann recht groß werden 😉 ), die Nodes heißen jetzt waggon. Ich bin ja wieder sooo kreativ 😃
    Die Erweiterung zum Tree und zur List stehen noch aus.

    Ich würde mich freuen, wenn ihr euch den Code mal anschaut, mir eure Meinung dazu sagt und Verbesserungsvorschläge gebt.

    Da ich hier aber eher ungern 550 Zeile Code posten möchte ist noch die Frage, wo ich den Code am besten hochlade...
    copyCode.de ist nicht so gut geeignet, die Code-Anzeige ist einfach zu schmal.
    Ist ideone.com besser? An sich brauchts ja keinen online-Compiler...

    Viele Grüße
    Cherup



  • Wenn du nichts findest, kann man hier auch über 500 Zeilen posten. Seiten, wo man schnell Code einfügen kann, gibts viele, mir fällt nur im Moment keine ein.



  • So, ich hau den Code mal hier rein 🙂

    Der Übersicht halber sind die Deklationen und Definitionen des Headers getrennt.

    Der Header:
    Deklarationen:

    #ifndef TRAINCONTAINER_H
    #define TRAINCONTAINER_H
    
    #include <map>
    #include <vector>
    #include <typeinfo>
    #include <typeindex>
    
    namespace trainContainer {
    class waggon_iterator;
    typedef const waggon_iterator const_waggon_iterator;
    
    class train{
    private:
        class waggon;
        friend class waggon_iterator;
    
    public:
        typedef std::map<std::type_index, std::vector<waggon*>>::iterator train_iterator;
        typedef std::map<std::type_index, std::vector<waggon*>>::const_iterator const_train_iterator;
    
    //structors
    public:
        train();
        ~train();
    
    //functions
    public:
        template<typename T> bool existType();
        template<typename T> bool existValue(T value);
        template<typename T> bool existDescription(T value);
    
        template<typename T> waggon_iterator insert(T value);
        template<typename T1, typename T2> waggon_iterator insert(T1 value, T2 description);
    
        //value can be value or description of waggon or iterators or type
        template<typename T> void remove();         //remove all waggons with valueType
        template<typename T> void remove(T value);  //remove waggon with value or description
        void remove(waggon_iterator waggon_it);         //remove waggon by iterator
        void remove(train_iterator train_it);         //remove all waggons by map-iterator
    
        void clear();
    
        //value can be value or description of waggon or train_iterators
        template<typename T> int count(T value);
    
        //value can be value or description of waggon
        template<typename T> waggon_iterator find(T value);
    
        //getting value by Description or waggon_iterator
        //returns the first value found
        template<typename T1, typename T2> T1 getValue(T2 description);
        template<typename T> T getValue(waggon_iterator waggon_it);
    
    //Functions iterating
    public:
        //begin() & end()
        template<typename T> waggon_iterator begin();
        waggon_iterator begin(std::type_index type);
        waggon_iterator begin(train_iterator it);
        waggon_iterator begin(const_train_iterator it);
    
        template<typename T> const_waggon_iterator begin() const;
        const_waggon_iterator begin(std::type_index type) const;
        const_waggon_iterator begin(train_iterator it) const;
        const_waggon_iterator begin(const_train_iterator it) const;
    
        template<typename T> waggon_iterator end();
        waggon_iterator end(std::type_index type);
        waggon_iterator end(train_iterator it);
        waggon_iterator end(const_train_iterator it);
    
        template<typename T> const_waggon_iterator end() const;
        const_waggon_iterator end(std::type_index type) const;
        const_waggon_iterator end(train_iterator it) const;
        const_waggon_iterator end(const_train_iterator it) const;
    
        train_iterator begin();
        const_train_iterator begin() const;
    
        train_iterator end();
        const_train_iterator end() const;
    
    //Classes
    private:
        class waggon{
        public:
            waggon();
            template<typename T> waggon(T value);
            template<typename T1, typename T2> waggon(T1 value, T2 description);
            ~waggon();
    
        public:
            bool hasDescription();
    
            template<typename T> T getValue();
            template<typename T> T getDescription();
    
            std::type_index getValueType();
            std::type_index getDescriptionType();
    
        private:
            class placeholder{
            public:
                virtual ~placeholder() {}
            };
    
            template<typename T>
            class holder : public placeholder{
            public:
                holder(T value) : myValue(value) {}
    
                T myValue;
            };
    
        private:
            placeholder* myValue = NULL;
            placeholder* myDescription = NULL;
    
            std::type_info* myValueType;
            std::type_info* myDescriptionType;
        };
    
    //Storage
    private:
        std::map<std::type_index, std::vector<waggon*>> myWaggons;
    };
    
    //Iterators
    class waggon_iterator
    {
    public:
        waggon_iterator() {}
        waggon_iterator(std::vector<train::waggon*>::iterator refIt) : myRefIterator(refIt) {}
    
        ~waggon_iterator() {}
    
        void operator ++()
        {
            myRefIterator++;
        }
        void operator ++(int)
        {
            ++myRefIterator;
        }
        void operator --()
        {
            myRefIterator--;
        }
        void operator --(int)
        {
            --myRefIterator;
        }
        void operator =(std::vector<train::waggon*>::iterator const& refIt)
        {
            myRefIterator = refIt;
        }
    
        bool operator ==(std::vector<train::waggon*>::iterator &rhs)
        {
            return myRefIterator == rhs;
        }
        bool operator !=(std::vector<train::waggon*>::iterator &rhs)
        {
            return !(myRefIterator == rhs);
        }
        bool operator ==(waggon_iterator &rhs)
        {
            return myRefIterator == rhs.Iterator();
        }
        bool operator !=(waggon_iterator rhs)
        {
            return !(myRefIterator == rhs.Iterator());
        }
    
        train::waggon* operator ->() const
        {
            return (*myRefIterator);
        }
        train::waggon* operator *()
        {
            return (*myRefIterator);
        }
    
        std::vector<train::waggon*>::iterator Iterator()
        {
            return myRefIterator;
        }
    
    private:
        std::vector<train::waggon*>::iterator myRefIterator;
    };
    
    }
    

    Definitionen:

    //Functions
    template<typename T> bool trainContainer::train::existType()
    {
        return myWaggons.count(typeid(T)) ? true : false;
    }
    
    template<typename T> bool trainContainer::train::existValue(T value)
    {
        if(existType<T>()){
            for(waggon_iterator it_waggon = myWaggons[typeid(T)].begin(); it_waggon != myWaggons[typeid(T)].end(); ++it_waggon){
                if(it_waggon->getValue<T>() == value) return true;
            }
        }
        return false;
    }
    
    template<typename T> bool trainContainer::train::existDescription(T value)
    {
        for(auto it_train = myWaggons.begin(); it_train != myWaggons.end(); ++it_train){
            for(waggon_iterator it_waggon = it_train->second.begin(); it_waggon != it_train->second.end(); ++it_waggon){
                if(it_waggon->hasDescription() &&
                   it_waggon->getDescriptionType() == typeid(T) &&
                   it_waggon->getDescription<T>() == value)
                    return true;
            }
        }
        return false;
    }
    
    template<typename T> trainContainer::waggon_iterator trainContainer::train::insert(T value)
    {
        myWaggons[typeid(T)].push_back(new waggon(value));
        waggon_iterator it = (myWaggons[typeid(T)].end()-1);
        return it;
    }
    
    template<typename T1, typename T2> trainContainer::waggon_iterator trainContainer::train::insert(T1 value, T2 description)
    {
        myWaggons[typeid(T1)].push_back(new waggon(value,description));
        waggon_iterator it = (myWaggons[typeid(T1)].end()-1);
        return it;
    }
    
    template<typename T> void trainContainer::train::remove(T value)
    {
     if(existValue<T>(value) ||
        existDescription<T>(value))
        {
            waggon_iterator it_wag = find<T>(value);
            delete *it_wag;
            myWaggons[std::type_index(typeid(T))].erase(it_wag.Iterator());
    
            if(myWaggons[std::type_index(typeid(T))].empty()) myWaggons.erase(std::type_index(typeid(T)));
        }
    }
    
    template<typename T> void trainContainer::train::remove()
    {
        if(existType<T>())
        {
            train_iterator it_train = myWaggons.find(std::type_index(typeid(T)));
            for(waggon_iterator it_wag = it_train->second.begin(); it_wag != it_train->second.end(); ++it_wag) delete *it_wag;
            it_train->second.clear();
            myWaggons.erase(it_train);
        }
    }
    
    template<typename T> int trainContainer::train::count(T value)
    {
        int counter = 0;
    
        if(typeid(T) == typeid(train_iterator) || typeid(T) == typeid(const_train_iterator)){
            counter = value->second.size();
        }
        else if(existValue<T>(value)){
            for(waggon_iterator it_waggon = myWaggons[typeid(T)].begin(); it_waggon != myWaggons[typeid(T)].end(); ++it_waggon){
                if(it_waggon->getValue<T>() == value) counter++;
            }
        }
        else if(existDescription<T>(value)){
            for(auto it_train = myWaggons.begin(); it_train != myWaggons.end(); ++it_train){
                for(waggon_iterator it_waggon = it_train->second.begin(); it_waggon != it_train->second.end(); ++it_waggon){
                    if(it_waggon->hasDescription() &&
                       it_waggon->getDescriptionType() == typeid(T) &&
                       it_waggon->getDescription<T>() == value)
                        counter++;
                }
            }
        }
        return counter;
    }
    
    template<typename T> trainContainer::waggon_iterator trainContainer::train::find(T value)
    {
        if(existValue<T>(value))
        {
            for(waggon_iterator it_waggon = myWaggons[typeid(T)].begin(); it_waggon != myWaggons[typeid(T)].end(); ++it_waggon){
                if(it_waggon->getValue<T>() == value) return it_waggon;
            }
        }
        else if(existDescription<T>(value))
        {
            for(auto it_train = myWaggons.begin(); it_train != myWaggons.end(); ++it_train){
                for(waggon_iterator it_waggon = it_train->second.begin(); it_waggon != it_train->second.end(); ++it_waggon){
                    if(it_waggon->hasDescription() &&
                       it_waggon->getDescriptionType() == typeid(T) &&
                       it_waggon->getDescription<T>() == value)
                       return it_waggon;
                }
            }
        }
        waggon_iterator it = myWaggons.begin()->second.end();
        return it;
    }
    
    template<typename T1, typename T2> T1 trainContainer::train::getValue(T2 description)
    {
        if(existDescription(description)){
            for(auto it_train = myWaggons.begin(); it_train != myWaggons.end(); ++it_train){
                for(waggon_iterator it_waggon = begin(it_train); it_waggon != end(it_train); ++it_waggon){
                    if(it_waggon->hasDescription() &&
                       it_waggon->getDescriptionType() == typeid(T2) &&
                       it_waggon->getDescription<T2>() == description)
                        return it_waggon->getValue<T1>();
                }
            }
        }
        return NULL;
    }
    
    template<typename T> T trainContainer::train::getValue(trainContainer::waggon_iterator waggon_it)
    {
        return waggon_it->getValue<T>();
    }
    
    /*--------------------------------------------------------------------------------------------------------------------------------------*/
    
    //iterators
    template<typename T> trainContainer::waggon_iterator trainContainer::train::begin()
    {
        return waggon_iterator(myWaggons[typeid(T)].begin());
    }
    
    template<typename T> trainContainer::const_waggon_iterator trainContainer::train::begin() const
    {
        return begin<T>();
    }
    
    template<typename T> trainContainer::waggon_iterator trainContainer::train::end()
    {
        return waggon_iterator(myWaggons[typeid(T)].end());
    }
    
    template<typename T> trainContainer::const_waggon_iterator trainContainer::train::end() const
    {
        return end<T>();
    }
    
    /*--------------------------------------------------------------------------------------------------------------------------------------*/
    
    //class waggon
    //structors
    template<typename T> trainContainer::train::waggon::waggon(T value)
    {
        myValue = new holder<T>(value);
    
        myValueType = const_cast<std::type_info*>(&typeid(T));
    }
    
    template<typename T1, typename T2> trainContainer::train::waggon::waggon(T1 value, T2 description)
    {
        myValue = new holder<T1>(value);
        myDescription = new holder<T2>(description);
    
        myValueType = const_cast<std::type_info*>(&typeid(T1));
        myDescriptionType = const_cast<std::type_info*>(&typeid(T2));
    }
    
    //functions
    template<typename T> T trainContainer::train::waggon::getValue()
    {
        return static_cast<holder<T>*>(myValue)->myValue;
    }
    
    template<typename T> T trainContainer::train::waggon::getDescription()
    {
        return static_cast<holder<T>*>(myDescription)->myValue;
    }
    
    #endif // TRAINCONTAINER_H
    

    Und noch die Definitionen der cpp:

    #include "trainContainer.h"
    
    //structors
    trainContainer::train::train()
    {
    
    }
    
    trainContainer::train::~train()
    {
        clear();
    }
    
    /*--------------------------------------------------------------------------------------------------------------------------------------*/
    
    //Functions
    void trainContainer::train::remove(waggon_iterator waggon_it)
    {
        std::type_index waggonType = waggon_it->getValueType();
        delete *waggon_it;
        myWaggons[waggonType].erase(waggon_it.Iterator());
    }
    
    void trainContainer::train::remove(train::train_iterator train_it)
    {
        for(auto it_wag = train_it->second.begin(); it_wag != train_it->second.end(); ++it_wag) delete (*it_wag);
        train_it->second.clear();
        myWaggons.erase(train_it);
    }
    
    void trainContainer::train::clear()
    {
        for(auto it_train = myWaggons.begin(); it_train != myWaggons.end(); ++it_train){
            for(waggon_iterator it_wag = it_train->second.begin(); it_wag != it_train->second.end(); ++it_wag){
                delete *it_wag;
            }
            it_train->second.clear();
        }
        myWaggons.clear();
    }
    
    /*--------------------------------------------------------------------------------------------------------------------------------------*/
    
    //Functions iterating
    trainContainer::waggon_iterator trainContainer::train::begin(std::type_index type)
    {
        return waggon_iterator(myWaggons[type].begin());
    }
    
    trainContainer::waggon_iterator trainContainer::train::begin(train::train_iterator it)
    {
        return waggon_iterator(myWaggons[it->first].begin());
    }
    
    trainContainer::waggon_iterator trainContainer::train::begin(train::const_train_iterator it)
    {
        return waggon_iterator(myWaggons[it->first].begin());
    }
    
    trainContainer::const_waggon_iterator trainContainer::train::begin(std::type_index type) const
    {
        return begin(type);
    }
    
    trainContainer::const_waggon_iterator trainContainer::train::begin(train_iterator it) const
    {
        return begin(it);
    }
    
    trainContainer::const_waggon_iterator trainContainer::train::begin(const_train_iterator it) const
    {
        return begin(it);
    }
    
    trainContainer::waggon_iterator trainContainer::train::end(std::type_index type)
    {
        return waggon_iterator(myWaggons[type].end());
    }
    
    trainContainer::waggon_iterator trainContainer::train::end(train_iterator it)
    {
        return waggon_iterator(myWaggons[it->first].end());
    }
    
    trainContainer::waggon_iterator trainContainer::train::end(const_train_iterator it)
    {
        return waggon_iterator(myWaggons[it->first].end());
    }
    
    trainContainer::const_waggon_iterator trainContainer::train::end(std::type_index type) const
    {
        return end(type);
    }
    
    trainContainer::const_waggon_iterator trainContainer::train::end(train_iterator it) const
    {
        return end(it);
    }
    
    trainContainer::const_waggon_iterator trainContainer::train::end(const_train_iterator it) const
    {
        return end(it);
    }
    
    trainContainer::train::train_iterator trainContainer::train::begin()
    {
        return myWaggons.begin();
    }
    
    trainContainer::train::const_train_iterator trainContainer::train::begin() const
    {
        return myWaggons.begin();
    }
    
    trainContainer::train::train_iterator trainContainer::train::end()
    {
        return myWaggons.end();
    }
    
    trainContainer::train::const_train_iterator trainContainer::train::end() const
    {
        return myWaggons.end();
    }
    
    /*--------------------------------------------------------------------------------------------------------------------------------------*/
    
    //class waggon
    //structors
    trainContainer::train::waggon::waggon() {}
    trainContainer::train::waggon::~waggon()
    {
        if(myValue != NULL) delete myValue;
        if(myDescription != NULL) delete myDescription;
    }
    
    std::type_index trainContainer::train::waggon::getValueType()
    {
        return std::type_index(*myValueType);
    }
    
    std::type_index trainContainer::train::waggon::getDescriptionType()
    {
        return std::type_index(*myDescriptionType);
    }
    
    bool trainContainer::train::waggon::hasDescription()
    {
        return myDescription != NULL ? true : false;
    }
    

    Verbesserungsvorschläge, Tipps usw. nehm ich gerne an.

    Viele Grüße und einen schönen Sonntag
    Cherup



  • Ich interpretiere das "train" mal eher als Training und nicht als Zug, weil so ganz erschließt sich mir der praktische Sinn davon in der Form noch nicht. Davon ausgehend seh ich auf den ersten Blick aber nichts, was mich großartig stören würde. Eher paar Kleinigkeiten:

    - In C++11 gibts endlich nullptr, NULL macht gar keinen Sinn mehr
    - Man darf Null Zeiger löschen, man muss nicht prüfen, ob die Null sind
    - Warum überhaupt etwas manuell löschen und nicht z.B. std::unique_ptr?
    - Der Sinn der "Description" erschließt sich mir nicht
    - Warum die const_casts? Speicher doch eine const type_info*. Wobei ich die nicht speichern, sondern eher live holen würde, wenn die jemand anfordert.
    - Ich würde grundsätzlich mehr auf Performance achten (muss nicht unbedingt sein, aber warum Rechenzeit verschwenden, wenn das nicht nötig ist?). Du rufst für exists z.B. count auf, obwohl du nur wissen willst, ob ein Element drin ist. Gibt noch mehr ähnlicher Stellen
    - Seh ich das richtig, dass du zwei Iteratoren hast? Ich hätt eher an einen Iterator gedacht, mit dem du entweder alles iterieren kannst, oder über das, was angefragt wurde. z.B. holt man sich einen Iterator für den Type int und kann mit dem nur über ints iterieren. Oder man holt sich einfach einen Iterator über alles, und iteriert mit dem dann transparent über alle Typen. Dann wäre der Iterator unabhängiger von der internen Struktur und würde sich eher an dem orientieren, was der Benutzer machen will.



  • Morgen,

    Danke für die Hinweise.
    - Die NULL-Pointer haben sich aus der vor-c++11-Zeit festgesetzt, muss mir das abgewöhnen. Die unique-ptr sind für die placeholder-Instanzen gut geeignet, aber für den vector<waggon*> würde ich eher den shared_ptr nutzen. Einfach aus dem Grund, dass ich dann zwar auch die Garbage-Collection-Funktionalität habe, aber einfacher mit den Pointern umgehen kann, z.B. bei der Übergabe an den Iterator.
    - Der Sinn von Description ist einfach ein zusätzlicher Parameter, um die Variablen anzusprechen wenn man die Reihenfolge nicht kennt, vergleichbar mit einem map-key.
    - Der const-Cast ist in der Tat überflüssig, hatte nicht daran gedacht die type_info als const zu speichern. Ich wüßte nicht, wie ich die type_info zur Laufzeit holen kann, dazu müsste ich den Typ kennen. Ich muss ja über placeholder auf die holder-Klasse zugreifen und dazu müsste ich die Klasse mit dem entsprechenden Typ casten...
    - Über die Peformance hatte ich auch schon nachgedacht aber nicht weiter verfolgt. Werde das noch verbessern.
    - Und der Iterator... Ja, an sich habe ich zwei, einen für die map und einen für die vectoren. Und du hast auch recht, dass ich in der jetzigen Form nur noch einen brauche. Ich hatte den einen Iterator geschrieben um das mal zu lernen (was ich an operatoren brauche, wie ich das mache usw.). Der zweite Iterator ist eher ein Überbleisel.

    Der Sinn des ganzen Containers ist zum einen das Lernen, zum anderen wollte ich einen Container haben, der eben so variabel wie dieser ist.
    Der Grund dafür ist, dass ich mein (Haupt-)Programm so dynamisch und einfach erweiterbar haben möchte wie möglich. Ich habe viele Klassen mit etlichen Attributen und ich möchte eine Möglichkeit haben, auf einfache Weise neue Attribute hinzuzufügen. Die Attribute stammen dabei aus Dateien und die Anzahl der Attribute und deren Werte sollen größtenteils erst zur Laufzeit bekannt sein.

    Viele Grüße und einen schönen Tag
    Cherup


Anmelden zum Antworten