Unterschiedliche Template-Ausprägungen eines Objekts in einem Container speichern



  • Hallo 🙂

    Der Titel sagt's fast schon, hier die Idee als Code:

    template <class T>
    class Object {
    public:
        Object(const std::string& name, T * pVar) : m_pData(pVar),
        m_name(name) {
    
        }
    
        void value(T val) {
            *m_pData = value;
        }
    
        void name(const std::string& name) {
            m_name = name;
        }
    
    private:
        T* m_pData;
        std::string m_name;
    };
    
    // Legt Objekte an
    class FriendlyHelper {
    public:
    
        template <class T>
        void remind(const std::string& name, T * pVar) {
            Object<T> obj(name, pVar);
            // Wie kann 'Obj' jetzt in einem Container gespeichert werden?
            // Identifikationsmerkmal waere der Name.
        }
    
        template <class T>
        void setVal(const std::string& name, T val) {
            // Zugriff z.B. durch lookup in einer Map:
            // map[name].value(val);
        }
    };
    
    int main() {
    
        int a = 17, b = 20;
    
        FriendlyHelper fh;
        fh.remind("Integer A", &a);
        fh.remind("Integer B", &b);
    
        // [...]
    
        // Irgendwann spaeter
        fh.setVal("Integer A", 300);
    
        //...
    }
    

    Ich bin also zwingend darauf angewiesen, den Typen zu kennen, damit ich den Wert später setzen kann.

    Ich komme da gerade auf keine schöne Lösung. Ist meine Idee überhaupt so realisierbar? Falls nein, was wäre ein Ansatz für eine Alternative? Danke 🙂



  • Speichern kannst du zwar, wenn du Object einer nichttemplateden Basisklasse erben lässt, aber setzen und auslesen geht schlecht typsicher. Zumindest nur mit dynamic_cast und auf den sollte man verzichten weil er langsam ist.

    Also sag lieber, wozu du das brauchst. Warum kann FriendlyHelper kein Template sein?


  • Mod

    aethbAM schrieb:

    Zumindest nur mit dynamic_cast und auf den sollte man verzichten weil er langsam ist.

    Vor allem deutet es auf einen schweren Designfehler hin, weil man in einer Situation ist, wo man man den dynamischen Typ eines Objekts kennen muss, diesen aber nicht kennt. Ein wahrscheinlicher Grund dafür ist, dass man zusammengeworfen hat, was nicht zusammen gehört.

    Also sag lieber, wozu du das brauchst.?

    +1



  • Okay, dann hier der Hintergrund:

    Ich möchte eine Klasse zur Verfügung stellen (der FriendlyHelper), die es ermöglicht, andere Klassen zur Laufzeit zu untersuchen und zu manipulieren. Beispiel: Der Nutzer hat folgende Klasse geschrieben:

    class IrgendeineKlasse {
    public:
    	IrgendeineKlasse() : a(17), b(20), c(1.333) {
    
    	}
    
    private:
    	  int a,b;
    	  float c;
    };
    

    Jetzt möchte der Nutzer Ausprägungen dieser Klasse gerne zur Laufzeit überwachen und ggfs. auch Variablen verändern. Hier meine Idee dazu:

    template <class T>
    class Object {
    public:
        Object(const std::string& name, T * pVar) : m_pData(pVar),
        m_name(name) {
    
        }
    
        void value(T val) {
            *m_pData = value;
        }
    
        void name(const std::string& name) {
            m_name = name;
        }
    
    private:
        T* m_pData;
        std::string m_name;
    };
    
    class FriendlyHelper {
    public:
    	FriendlyHelper(const std::string& className) : m_className(className) {
    		globalController->registerHelperClass(this);
    	}
    
        template <class T>
        void remind(const std::string& name, T * pVar) {
            Object<T> obj(name, pVar);
            // Wie kann 'Obj' jetzt in einem Container gespeichert werden?
            // Identifikationsmerkmal waere der Name.
        }
    
        template <class T>
        void setVal(const std::string& name, T val) {
            // Zugriff z.B. durch lookup in einer Map:
            // map[name].value(val);
        }
    
    private:
    	std::string m_className;
    };
    

    Wer jetzt schnell gescrollt hat: Es gibt zwei Änderungen. FriendlyHelper wird ein String im ctor übergeben und im ctor meldet sich der FriendlyHelper bei einem globalen Controller globalController an.
    Jetzt ist folgendes möglich:

    class IrgendeineKlasse : public FriendlyHelper {
    public:
    	IrgendeineKlasse() : FriendlyHelper("IrgendeineKlasse"), a(17), b(20), c(1.333) {
    		remind("Integer A", &a);
    		remind("Integer B", &b);
    		remind("Float C", &c);
    	}
    
    private:
    	  int a,b;
    	  float c;
    };
    

    Der Nutzer erbt also einfach vom FriendlyHelper und meldet dort an, was er gerne überwacht hätte bzw. welche Variablen er preisgeben möchte.

    Und so kann man dann darauf zugreifen:

    globalController.setValue("IrgendeineKlasse.Integer B", 300);
    	int val = globalController.getValue("IrgendeineKlasse.Integer B");
    

    Anhand des Pfades kann geprüft werden, ob solch eine Variable überhaupt existiert. Wir ignorieren jetzt mal den Fall, das mehrere Objekte eines Typs angelegt werden können. In diesem Fall würde dann eben eine ID vergeben.

    ------
    Das war mein genereller Ansatz und dafür brauche ich die beschriebene Funktionalität. Das Design ist also Mist? Ok, ich bin lernwillig und aufgeschlossen, wie kann ich das lösen? 😕 😕



  • Aus einem anderem Thema hier im Forum:

    seldon schrieb:

    Die Notwendigkeit, den genauen Typ eines Objektes zur Laufzeit herausfinden zu müssen, widerspricht dem objektorientierten Gedanken der Kapselung völlig - der Client-Code soll die Implementationsdetails der Objekte, mit denen er arbeitet, nicht kennen müssen, sondern lediglich ihre Schnittstelle.

    Ja, genau das will ich, die Kapselung durch den Controller aufheben bzw. Implementationsdetails bekanntmachen. Wobei in meinem Fall ja jede Klasse immer noch selbst bestimmen kann, was welche Variablen sie bekanntmachen will.

    Wie gesagt, für andere Lösungen bin ich sehr offen, aber es geht schon darum, an die interna heranzukommen.


Log in to reply