map mit unterschiedlichen type



  • hallo zusammen,

    ich hätte gern eine map, in welcher ich per string key verschiedenen typen gemeinsam speichern kann. also so im dem sinne:

    A a;
    B b;
    std::map<std::string, ???> storage;
    
    storage.at("a", a);
    storage.at("b", b);
    

    bisher habe ich hinweise auf boos.any bzw. boost.variant gefunden. bin mir da aber nicht sicher. gibt es in c++11 was feines??? erfahrungen? meinungen???



  • bzw. ist die verwendungen des "property patterns" besser, als ne map? aus den boost examples:

    struct property
    {
        property();
        property(const std::string &, const boost::any &);
    
        std::string name;
        boost::any value;
    };
    
    typedef std::list<property> properties;
    


  • Was hast du denn vor?

    Erstmal hört sich das komisch an, wenn du komplett unterschiedliche Typen in einer Datenstruktur zusammenfassen willst und zeugt von nicht durchdachtem Design.
    Umgehen kann man das z.B. per gemeinsamer Basisklasse und Polymorphie.

    Klar, es gibt auch Anwendungsfelder für boost::any und dergleichen.

    Daher, erklär mal, was du vorhast.



  • hey,

    also ich habe diverse timer, mit sub- und zwischenklassen. die unterscheiden sich oft nur durch wenige details. vorallem werden sie aus semantischen gründen genutzt. nun ist die idee, dass man die basisklasse timer um so eine property-list. die einzelnen timer würden dann nur noch über die getter und setter auf die liste zugreifen. dadurch erhoffe ich mir ne einfachere struktur, gerade für member die ähnlich sind und dann aber wieder doch nicht. auch das ganze ein wenig leichtgewichtiger in der nutzung. verständlicher?



  • Wie gesagt, das hört sich eher nach Vererbung mit Polymorphie und sowas an.



  • dann anders gefragt,

    ihr habt sagen wir acht timer, welche aus logisch unterschiedlichen modulen stammen. diese unterscheiden sich semantisch, aber aber kreuz die quere ähnliche abhängigkeiten. vererbung zwischen den modulen geht nicht, da nicht immer alle da sind, und es auch nen doofes design ist. alles in die oberklassen schieben ist doof, weil diese unnötig fett werden. zwischenklassen einführen führt zu quasi identischen klassen, in unterschiedlichen modulen.

    mh 😕



  • Das Problem ist, dass storage["bla"] nicht weiß was es zurückgeben soll. Könnte ja jeder der 8 Timer sein. Da gibt es verschiedene Lösungen:
    -Benutze eine map pro Timertyp
    -Benutze eine map mit void * und finde dann selbst raus mit welchem Timertyp du es zu tun hast
    -Schreibe ein struct UnitedTimer, der intern einen void * auf den Timer und eine ID für den Timertyp hält und getTime und setTime anbietet, was je nach Timertyp anders implementiert ist, und dann einfach map<string, UnitedTimer>

    Interessiert es dich überhaupt welche Art von Timer du gerade hast oder willst du nur die Zeit wissen?

    Die korrekte Lösung ist natürlich die offensichtliche: Schmeiße 7 der 8 Timertypen weg und benutze nur noch einen. Aber das macht ja Arbeit 🤡



  • ne, da missverstehen wir uns ein wenig.

    [/code]
    basistimer:

    ...
    protected:
    map<string, any> storage;

    subtimer b:

    getProtery1(){

    return any_cast<TYPE_OF_PROPERTY_1>(storage["PROPERTY_1"]);
    }

    setPorperty1(TYPE_OF_PROPERTY_1 prop){
    storage["PROPERTY_1"]= prop;
    }
    [/code]

    Die timer überwachen in einem protokoll verschiedene aktionen (connection timeout, retransmission, leaf, join, ....). je nach submobdul halt. da nicht immer alle module da sind, kann ich auch nicht einfach timer raus oder rein nehmen.

    im moment habe ich eine lange kette von vererbung etc. dabei kommt es aber vor, dass in bestimmten contexten die eigenschaften nicht notwendig sind beim timer, in anderen schon. heißt, er schleppt im worst case eine latte von ungenutzten parametern mit. zum beispiel eine anzahl von listen. dies will ich halt mir sparen, weil es auch immer schwieriger wird, durchzusehen, wo wann welche eigenschaft genutzt wird.



  • Mal anders gefragt: Was haben all die Timer denn gemeinsam? Weswegen willst du die alle in eine gemeinsame Map packen?



  • sven_ schrieb:

    dann anders gefragt,

    ihr habt sagen wir acht timer, welche aus logisch unterschiedlichen modulen stammen. diese unterscheiden sich semantisch, aber aber kreuz die quere ähnliche abhängigkeiten. vererbung zwischen den modulen geht nicht, da nicht immer alle da sind, und es auch nen doofes design ist. alles in die oberklassen schieben ist doof, weil diese unnötig fett werden. zwischenklassen einführen führt zu quasi identischen klassen, in unterschiedlichen modulen.

    mh 😕

    Also brauchste nur 8 Namen, das sind 8 Variablen/Zeiger/Referenzen/Zugriffsfunktionen.



  • sven_ schrieb:

    dann anders gefragt,

    ihr habt sagen wir acht timer, welche aus logisch unterschiedlichen modulen stammen. diese unterscheiden sich semantisch, aber aber kreuz die quere ähnliche abhängigkeiten. vererbung zwischen den modulen geht nicht, da nicht immer alle da sind, und es auch nen doofes design ist. alles in die oberklassen schieben ist doof, weil diese unnötig fett werden. zwischenklassen einführen führt zu quasi identischen klassen, in unterschiedlichen modulen.

    mh 😕

    Mal angenommen, es existiert eine map mit der Eigenschaft:

    map< string, Any > storage;
        TimerA tmrA;
        TimerB tmrB;
        storage.insert( "a", tmrA );
        storage.insert( "b", tmrB );
        // ...
    

    wie möchtest Du dann auf den Inhalt von storage["a"] zugreifen. 'Weiß' der Code, dass sich dahinter nur ein Typ TimerA verbergen kann?

    Können in einem storage auch zwei Objekte mit gleichem Typ vorkommen - oder sind alle Typen zwangsläufig unterschiedlich?



  • ne, da haben wir uns missverstanden. in die map will ich member der timer stecken. ich habe zum beispiel folgende kette:

    TimerA <- TimerB <- TimerC

    wobei TimerB und TimerC jeweils den Timer erweitern. Dann in einem andern Modul habe ich:

    TimerA <- TimerD <- TimerE

    wobei TimerD und TimerB nix logisch miteinander zu tun haben. Wenn man dann sich die Verwendung von TimerC anschaut, gibt es halt UseCases wo alle Eigenschaften von TimerA, TimerB und TimerC belegt sind, und welche, wo es nur ausgewählte sind.

    Wenn ich nun schauen will, ob ich Member einsparen kann, muß ich alle Instanzen und Szenarien durchschauen. Mit der Map könnte man sich auf die spez. Getter/Setter zurückziehen.

    Verständlicher?



  • zur nutzung nochmal der pseudocode in schön:

    class Timer{
    
     ... 
    
    protected: 
     map<string, any> storage; 
    }
    
    class SubTimer { 
    
    public: 
    TYPE_OF_PROPERTY_1 getProtery1(){ 
    
     return any_cast<TYPE_OF_PROPERTY_1>(storage["PROPERTY_1"]); 
    } 
    
    void setPorperty1(TYPE_OF_PROPERTY_1 prop){ 
     storage["PROPERTY_1"]= prop; 
    } 
    }
    

    Besser?



  • natürlich so:

    class SubTimer : public Timer{..}
    


  • Musst du zur Laufzeit drauf zugreifen oder reicht es aus, beim Programmstart die Eigenschaften zu lesen? Wenn du das nur selten machst könntest du die Eigenschaften in einen string serialisieren und beim Lesen den string lesen, parsen und die entsprechenden Eigenschaften setzen.



  • da es timer sind, werden diese eigenschaften natürlich zur laufzeit gesetzt bzw. gelesen.



  • sven_ schrieb:

    da es timer sind, werden diese eigenschaften natürlich zur laufzeit gesetzt bzw. gelesen.

    Gut, ich hab mich vielleicht etwas unglücklich ausgedrückt, natürlich müssen die Eigenschaften zur Laufzeit ausgelesen werden. Musst du die Eigenschaften häufig und/oder zeitkritisch auslesen oder kannst du es dir leisten, strings zu parsen?



  • na es kommt halt eine nachricht rein, welche die eigenschaften des timers ändert. da es ein kommunikationsprotokoll ist, sollte es schon flott gehen. ist aber keine high-performance anwendung.



  • Nochmal: Was haben die Dinger, die du in eine gemeinsame Map packen möchtest, gemeinsam?


Log in to reply