unique_ptr aus map auslesen und wo ist make_unique?



  • wob schrieb:

    2. Wenn du ein Template nimmst, dann könnte man mit deinem Code einen Katalog von int machen und zur Laufzeit(!) bekommst du dann eine Nullpointer-Dereferenzierung, weil int* in Object* gedynamiccastet wird und das ergibt nullptr).

    Hab ich grad ausprobiert, das kompiliert nicht.

    Catalog<int> c2;
    c2.add(new int{42});
    

    Fehler: cannot dynamic_cast 'object' (of type 'int*') to type 'class tad::Object*' (source is not a pointer to class)
    map.insert(std::make_pair<ulong, std::unique_ptr<T>>(dynamic_cast<Object*>(object)->getId(), std::unique_ptr<T>(object)));



  • firefly schrieb:

    Th69 schrieb:

    C++ templates sind unabhängig von irgend einer Klassenhierarchie.

    Hier bist du einem missverständnis aufgesessen.
    ...

    Hä??? Ich weiß, wie C#-Generics funktionieren - und ich schrieb, daß eben bei den C++ Templates der zu verwendende Typ nicht von irgendeiner Klassenhierarchie (wie eben bei den C# Generics) abhängig ist.

    Und @temi:

    template<class T> T* Catalog<T>::find(ulong id) const
    {
        return map.at(id).get(); // 1. dies funktioniert
    
        auto const &it = map.at(id); // 2. so funktioniert das!
        return it != map.end() ? it.get() : nullptr;
    
        return map[id].get(); // 3. das funktioniert auch nicht - kann auch nicht, denn dies funktioniert nur bei nichtkonstanten Funktionen
                              // (denn bei Bedarf wird hier ein Element der map hinzugefügt).
    }
    


  • Th69 schrieb:

    firefly schrieb:

    Th69 schrieb:

    C++ templates sind unabhängig von irgend einer Klassenhierarchie.

    Hier bist du einem missverständnis aufgesessen.
    ...

    Hä??? Ich weiß, wie C#-Generics funktionieren - und ich schrieb, daß eben bei den C++ Templates der zu verwendende Typ nicht von irgendeiner Klassenhierarchie (wie eben bei den C# Generics) abhängig ist.

    Deine Antwort klang aber anders, sorry wenn ich dich falsch verstanden haben sollte



  • Ich hab es jetzt so gemacht. Damit ist der Iterator auch wirklich ein Iterator. Danke an wob für die Erleuchtung.

    template<class T> T* Catalog<T>::find(ulong id) const
    {
        auto it = map.find(id);
        return it != map.end() ? it->second.get() : nullptr;
    }
    

    Ich würde mich dennoch über Antworten, meine weiteren Überlegungen betreffend, freuen (siehe oben).

    Speziell dieses würde mich recht reizen.

    auto l = catalog.find(42).asLocation();
    


  • temi schrieb:

    Hab ich grad ausprobiert, das kompiliert nicht.

    Ok, da habe ich zu viel vereinfacht. Es braucht schon einen Klassentypen. Probiere doch statt int mal
    struct Foo { int i };
    aus und setzte new Foo statt new int ein.



  • wob schrieb:

    temi schrieb:

    Hab ich grad ausprobiert, das kompiliert nicht.

    Ok, da habe ich zu viel vereinfacht. Es braucht schon einen Klassentypen. Probiere doch statt int mal
    struct Foo { int i };
    aus und setzte new Foo statt new int ein.

    Fehler: cannot dynamic_cast 'object' (of type 'struct Foo*') to type 'class tad::Object*' (source type is not polymorphic)
    map.insert(std::make_pair<ulong, std::unique_ptr<T>>(dynamic_cast<Object*>(object)->getId(), std::unique_ptr<T>(object)));



  • temi schrieb:

    Fehler: cannot dynamic_cast 'object' (of type 'struct Foo*') to type 'class tad::Object*' (source type is not polymorphic)
    map.insert(std::make_pair<ulong, std::unique_ptr<T>>(dynamic_cast<Object*>(object)->getId(), std::unique_ptr<T>(object)));

    Dann spendiere dem Foo halt noch eine virtuelle Funktion!



  • wob schrieb:

    temi schrieb:

    Fehler: cannot dynamic_cast 'object' (of type 'struct Foo*') to type 'class tad::Object*' (source type is not polymorphic)
    map.insert(std::make_pair<ulong, std::unique_ptr<T>>(dynamic_cast<Object*>(object)->getId(), std::unique_ptr<T>(object)));

    Dann spendiere dem Foo halt noch eine virtuelle Funktion!

    Ich gebe auf.

    Tad3 ist abgestürzt

    static_assert(std::is_base_of<Object, T>::value, "Catalog<T>: T is not derived from Object");
    

    Hat geklappt. 👍



  • Das ist jetzt eher eine theoretische Spielerei, aber es würde mich dennoch interessieren. Ich schreibe die Frage in dieses Thema, weil sie eine Ergänzung zur vorherigen Diskussion ist.

    // --- diese Lösung funktioniert
    
        Catalog catalog{new Converter}; // Der Catalog bekommt einen Converter für die Umwandlung der Rückgabewerte mit
    
        catalog.add(new Item{"Foo"}); // neues Item : public Object => Catalog kann nur Object* speichern!
        catalog.add(new Location{"Bar"}); // neue Location : public Object
    
        catalog.find(1).asItem(); // .find liefert den Converter zurück und dieser enthält die Funktion .asItem()
        catalog.find(2).asLocation(); // dito nur diesmal ist es die Funktion .asLocation()
    
        // werden neue Typen benötigt, dann muss nur der Converter angepasst werden
    
        // --- Ist das irgendwie umsetzbar???
    
        Catalog catalog{};
    
        catalob.AddConverter{new ItemConverter}; // dieser Katalog kann nur "Items" zurückgeben
    
        catalog.addObject(new Item{"Foo"});
    
        catalog.find(1).asItem();
    
        catalog.addConverter{new LocationConverter}; // jetzt kann er "Items" und "Locations" zurückgeben
    
        catalog.addObject(new Location{"Bar"});
    
        catalog.find(1).asLocation(); // nullptr muss vom Aufrufer behandelt werden!
        catalog.find(2).asLocation();
    
        // zur Erweiterung des Katalogs müsste ein zusätzlicher Konverter hinzugefügt werden
    

    Ich habe mich bereits nach möglichen Lösungen umgeschaut, aber bisher noch nichts Brauchbares gefunden. Die Konverter müssten in einer Liste gespeichert werden, durch die iteriert und der passende Konverter aufgerufen wird. Knackpunkt ist der Rückgabewert von .find(), denn der würde dann dem jeweils gefundenen Konverter entsprechen müssen.

    Gruß,
    temi



  • Ich habe jetzt "meine" Lösung gefunden und glaube dass sie ganz gut gelungen ist.

    class Catalog
    {
        public:
            Catalog() {};
            void add(Object* object);
            template<class T> bool exists(ulong id);
            template<class T> T* get(ulong id);
        private:
            std::unordered_map<ulong, std::unique_ptr<Object>> map;
    };
    
    template<class T> bool Catalog::exists(ulong id)
    {
        static_assert(std::is_base_of<Object, T>::value, "Catalog.exists<T>(id): T is not derived from Object");
    
        return get<T>(id) != nullptr;
    }
    
    template<class T> T* Catalog::get(ulong id)
    {
        static_assert(std::is_base_of<Object, T>::value, "Catalog.get<T>(id): T is not derived from Object");
    
        return dynamic_cast<T*>(map[id].get());
    }
    

    Damit kann ich beliebige von "Object" abgeleitete Objekte speichern und erhalte den korrekten Typen über ein klares Interface wieder zurück.

    int main(int argc, char *argv[])
    {
        Catalog catalog;
    
        catalog.add(new Item{"Axt"});
        catalog.add(new Item{"Baum"});
        catalog.add(new Location{"Wald"});
    
        if (catalog.exists<Item>(42))
            cout << catalog.get<Item>(42)->getName() << endl;
        else cout << "Can't fing matching item" << endl;
    
        cout << catalog.exists<Item>(1) << endl;
        cout << catalog.exists<Item>(2) << endl;
        cout << catalog.exists<Item>(3) << endl;
        cout << catalog.exists<Location>(1) << endl;
        cout << catalog.exists<Location>(2) << endl;
        cout << catalog.exists<Location>(3) << endl;
    
        cout << catalog.get<Item>(1)->getName() << endl;
        cout << catalog.get<Item>(2)->getName() << endl;
        cout << catalog.get<Location>(3)->getName() << endl;
    }
    

    Über Lob oder Kritik würde ich mich weiterhin freuen. Es lässt sich einfacher lernen, wenn man auf Fehler hingewiesen wird und diese dann in Zukunft vermeiden kann. Außerdem wäre ich auch weiterhin an Antworten auf die eine oder andere Frage aus den vorherigen Beiträgen interessiert, obwohl ich das meiste davon bereits wieder verworfen habe.

    Danke und Gruß,
    temi


Anmelden zum Antworten