Templatetyp innerhalb von Methode festlegen



  • Hey!

    Ich habe eine Klasse, die einen void*-Pointer hat. Gleichzeitig gibt es eine Variable, die sich den zugehörigen Typen zum Pointer merkt, wenn z.B. der Pointer auf int zeigt steht in der Variablen sinngemäß "INT". (Fragt bitte nicht wofür man so eine Klasse braucht).
    Nun will ich an den Wert herankommen, dies aber möglichst simpel halten.
    Eine GetVar()-Methode soll reichen, aber ich weiß nicht wie ich dem Compiler so etwas verkaufen könnte:

    template<typename T>
    T GetVar()
    {
    	switch(GetType())
    	{
    		case INT:
    		return(*static_cast< const int* >(m_pData)); 
    		break;
    	}
    }
    

    Und ich kann keine templateklasse aus der Klasse machen, weil ich sie sonst nicht in den stl-Containern verwalten kann 🙄

    Muss ich jetzt wirklich für jeden Datentyp eine GetVarAsInt(), GetVarAsBool() usw. erstellen ? ⚠ ?



  • Templates müssen zur Compilezeit wissen, welchen Typ sie gerade verwenden wollen. Wenn du den Typ zur Laufzeit ändern willst, benötigst du z.B. unions oder polymorphe Typen (Stichwort: Vererbung und virtuelle Methoden).



  • Wie wäre denn grob das Prinzip wenn ich das mit Vererbung machen würde?

    Datentyp

    und dann

    Int : public Datentyp

    und dann GetVar usw. überschreiben?

    Und wie wäre das bei unions?



  • Nach einschläger Recherche bin ich zu dem Ergebnis gekommen, dass die GetVar()-Methode nicht mit einer union realisiert werden kann.
    Zumindest wüsste ich nicht wie ➡ ?



  • Nimm boost::any und erfinde das Rad nicht neu (dazu wäre deins schlechter :p).



  • Kann keine Third Party Libs benutzen. Dennoch Danke.

    Nur nochmal für die Statistik: Man kann keine

    union
    

    zurückgeben nicht wahr?



  • *nerv*

    Es geht ja noch nicht einmal sowas hier:

    template<typename T> 
    			virtual T GetVar() = 0;
    

    Also geht's auch nicht mit Polymorphismus. Na ja, dann gehts eben nicht 😡



  • virtual und template? Das dürfte sich irgendwie beißen 😉 (virtual bestimmt zur Laufzeit, welche Version einer bestimmten Methode aufgerufen wird (wahlweise "int Base::GetVal()" oder "int Derived::GetVal()"), templates werden zur Compilezeit ausgewertet)

    Wenn du keine externe Bibliothek verwenden kannst (wer verbietet dir eigentlich boost?), mußt du dir wohl selber etwas vergleichbares bauen, z.B. so etwas:

    template<typename T1,typename T2>
    struct p_union
    {
      bool typ;//true=T1,false=T2
      union
      {
        T1 val1;
        T2 val2;
      } val;
    };
    
    p_union<int,double> un;
    ...
    if(un.typ)
    {
      int i=un.val.val1;
      ...
    }
    else
    {
      double d=un.val.val2;
      ...
    }
    


  • Und genau um dieses:

    if(un.typ) 
    { 
      int i=un.val.val1; 
      ... 
    } 
    else 
    { 
      double d=un.val.val2; 
      ... 
    }
    

    geht es mir hier. Ich muss mir als weiterführender Programmierer anschauen, von welchem Typ die Struktur ist und dementsprechend die enthaltene Variable dem richtigen Typ zuweisen.
    Das geht, geht momentan auch mit meiner Methode, will ich aber irgendwie vermeiden. Mein Gedanke war, dass diese Struktur irgendwie eine einzelne GetVar() bekommt, und GetVar soll dann entscheiden welcher Typ enthalten ist und dann das richtige zurückliefern.

    Nur scheint das wohl nicht zu gehen. @CStoll, wenn du (der ja mehr Erfahrung hat als ich 😉 ) mir jetzt sagst, dass das wirklich nicht geht, dann kann ich mir das weitere Grübeln auch sparen 🙂 👍



  • Es geht nicht, daß sich der Rückgabetyp einer Funktion zur Laufzeit verändert - der wird einmal festgelegt und bleibt dann konstant. Du könntest höchstens den oben angegebenen p_union-Typ soweit aufpeppeln, daß er je nach Bedarf entweder als T1 oder T2 reagiert (indem du diese "if(typ)...else..."-Abfrage in die Zugriffmethoden reinziehst; es dürfte aber sehr schwer sein, alle benötigten Operationen dort unter einen Hut zu bekommen).

    PS: Google mal nach "Variant C++" - da könntest du auch etwas für dich geeignetes finden 😉



  • Manchmal hilft beim Verständnis ein Anwendungsbeispiel. Nehmen wir an, Du schaffst es, eine solche Klasse, nennen wir sie man Variant, zu schreiben, mit einer getValue-Methode mit Template-Rückgabewert. Die Anwendung wäre dann:

    Variant v;
    int i;
    
    v.setValue(1);  // wir setzen das Ding auf int
    i = v.getValue();  // ist ok
    
    v.setValue("Hallo");   // jetzt ist es ein const char*
    i = v.getValue();      // hier soll es aber einen Compile-Fehler ergeben???
    

    Tommi



  • Ist zwar nicht als Rückgabewert aber auch nicht sonderlich umständlich:

    class CVariant
    {
    
    public:
    
    	enum EType
    	{
    		ET_NONE,
    		ET_INT,
    		ET_STRING,
    	};
    
    private:
    
    	EType eType;
    	union
    	{
    		int   i;
    		char* c;
    	} u;
    
    public:
    
    	inline CVariant()
    	  : eType(ET_NONE)
    	{
    	}
    
    	inline CVariant (int i)
    	  : eType(ET_INT)
    	{
    		this->u.i = i;
    	}
    
    	inline CVariant (char* c)
    	  : eType(ET_STRING)
    	{
    		this->u.c = c;
    	}
    
    	inline EType GetType() const
    	{
    		return this->eType;
    	}
    
    	bool Get (int& i) const;
    	bool Get (char*& c) const;
    	void Set (int i);
    	void Set (char* c);
    };
    
    bool CVariant::Get
    	(int& i) const
    {
    	if (this->eType == ET_INT)
    	{
    		i = this->u.i;
    		return true;
    	}
    
    	i = 0;
    	return false;
    }
    
    bool CVariant::Get
    	(char*& c) const
    {
    	if (this->eType == ET_STRING)
    	{
    		c = this->u.c;
    		return true;
    	}
    
    	c = 0;
    	return false;
    }
    
    // die CVariant::Set kannst du dir denken
    

    Ist aber schnell aus dem Arm geschüttelt und nicht getestet 😛

    *edit* Noch die Aufrufe:

    CVariant v (5);
    int i;
    
    if (v.Get (i))
    	...; // v enthält ein int
    else
    	...; // v enthält kein int
    


  • Immerkomplizierter schrieb:

    Und ich kann keine templateklasse aus der Klasse machen, weil ich sie sonst nicht in den stl-Containern verwalten kann 🙄

    hm, könnte es nicht sein, dass es folgendes ist, was Du suchst?

    class VariantBase {
        public:
            VariantBase() {}
            virtual ~VariantBase() {}
            virtual void getValue() = 0;
    };
    
    template<typename T>
    class Variant : public VariantBase {
        public:
            Variant(T value) : m_Value(value) {
            }
            virtual T getValue() {
                return m_Value;
            }
        private:
            T m_Value;
    };
    

    So kannst Du einen STL-Container erstellen, in dem Du diesen von der Basisklasse erstellst, die keine Templateklasse ist und durch die virtuelle getValue-Methode erhälst Du immer genau den richtigen Typ. Also z.B. so:

    std::vector<BaseVariant*> vec;
    vec.push_back( new Variant<int>(3) );
    vec.push_back( new Variant<double>(3.41) );
    ...
    
    // löschen nicht vergessen :)
    for (int i = 0; i < vec.size(); i++) {
          delete vec[i];
    }
    

    // Edit: zu langsam. 🙂
    obige Methode ist auch nicht schlecht. 🙂



  • Hm, wie holt man den Wert aus dieser Variant raus?

    double d = vec[0]->getValue(); // ups, getValue ist void
    

    Das:

    if (Variant<int>* vint = dynamic_cast<Variant<int>*>(vec[0])) {
      int i = vint->getValue();
    }
    

    kanns ja irgendwie nicht sein 😉



  • Hehe, ihr seid gut 😉 ⚠

    Momentan arbeite ich an einer Lösung aus template und Vererbung. Die läuft auch, dazu noch die Getmethoden von Neku und ich hab das was ich wollte.

    Klasse jungs 😋 🤡



  • Eleganter als die Neku-Variante wäre eine Template-Spezialisierung:

    class Variant
    {
      public:
        template <typename T>
          T getValue() const;       // keine Implementierung
        template <>
          int getValue<int>()
          {
            if (getType() != "INT")
              throw std::runtime_error("falscher Typ");
            return was_auch_immer;
          }
        template <>
          int getValue<std::string>()
          {
            if (getType() != "STRING")
              throw std::runtime_error("falscher Typ");
            return was_auch_immer;
          }
    
    };
    
    Variant v;
    v = 4;
    int i = v.getValue<int>();
    double d = v.getValue<double>();  // compile-error - keine Template-Spezialisierung
    

Anmelden zum Antworten