Typ zur Laufzeit ermitteln



  • Servus,

    Introspektion ist ja in C++ nicht so direkt machbar wie etwa in Java. Ich will aber gerne nur an einer Stelle festlegen ob mein Programm ein set<string> oder einen vector<string> nimmt - das ist aber blöd, denn je nachdem was zur Laufzeit genommen wird, muss die Methoden entweder push_back() oder insert() heissen. Wie macht man das dann normalerweise?

    Der Weg über Qt ist für mich keiner da Ubuntu Qt 10.04 und Qt nicht so will. Generell ist mir ein Qt-unabhängiger Weg aber auch sympathischer für die reine Typermittlung zur Laufzeit.

    Ich nutze aktuell eine überladene Funktion und vorher ein typedef, in der Theorie schaut das so aus:

    typedef set<string> myContainer;
    // typedef vector<string> myContainer;
    
    string inspectMe( set<string> para ){ return "set"; }
    string inspectMe( vector<string> para ){ return "vector"; }
    
    // in main() steht dann
    typeOpt1 = "set";
    typeOpt2 = "vector";
    myContainer mycon;
    typeActual = inspectMe( mycon );
    if ( typeActual == typeOpt1 ) { .... set war zur Laufzeit da ...}
    if ( typeActual == typeOpt2 ) { .... vector warzur Laufzeit da ...}
    

    Soweit in der Theorie, in der Praxis strauchle ich dass das mit den Container und dem typedef denke ich noch nicht richtig ist, Compiler meckert in der Zeile mit dem 'typeActual = inspectMe( myContainer );':
    main.cpp:1217: error: expected primary-expression before ‘)’ token



  • Ah, ging doch - einfacher Tippfehler beim kompilieren!



  • Ich glaube nicht, dass du den Typ zur Laufzeit bestimmen willst.
    Er hat ja zur Laufzeit immer den selben Typ.

    Du suchst wohl eher nach etwas wie:

    #include<set>
    #include<vector>
    #include<iostream>
    #include<string>
    
    typedef std::set<std::string> myContainer;
    
    template<typename T>
    void insertInto(std::vector<T>& vector, const T& t){
            vector.push_back(t);
            std::cout << "Adding " << t << " to vector" << std::endl;
    }
    
    template<typename T>
    void insertInto(std::set<T>& set, const T& t){
            set.insert(t);
            std::cout << "Adding " << t << " to set" << std::endl;
    }
    
    template<typename T>
    void doStuff(T& collection){
            insertInto(collection, std::string("myString 1"));
            insertInto(collection, std::string("myString 2"));
    }
    
    int main(int argc, char**) {
            std::set<std::string> set;
            std::vector<std::string> vector;
            myContainer container;
            doStuff(set);
            doStuff(vector);
            doStuff(container);
    }
    

    Aber eigentlich hast du sowas ja schon bei inspectMe verwendet...

    Gruß,
    XSpille



  • Jay1980 schrieb:

    Ich will aber gerne nur an einer Stelle festlegen ob mein Programm ein set<string> oder einen vector<string> nimmt - das ist aber blöd, denn je nachdem was zur Laufzeit genommen wird, muss die Methoden entweder push_back() oder insert() heissen. Wie macht man das dann normalerweise?

    Methodenüberladung oder Templates. Entscheidungen zur Laufzeit zu treffen, ist grundsätzlich langsamer als zur Compilezeit.

    Zudem solltest du dir in deinen Code noch andere Dinge angewöhnen, z.B. das unnötige Kopieren von Containerklassen vermeiden:

    // Hier übergibst du eine Kopie, was in dem Fall wohl Unsinn ist.
    string inspectMe( set<string> para ){ return "set"; }
    // Besser per Referenz / in diesem Fall, da du am Set wohl nichts ändern willst
    // konstant...
    string inspectMe( set<string> const & para ){ return "set"; }
    

    Noch besser wäre aber das gänzliche Vermeiden unnötiger Typinformationen. Wenn es nur um die unterschiedliche Abbildung der Einfügeroutine geht, wäre der Weg über eine Überladene Funktion wohl wirklich nicht verkehrt.

    #include <string>
    #include <set>
    #include <vector>
    
    typedef set<string> myContainer;
    // typedef vector<string> myContainer;
    
    void insert(std::set<string> & para, std::string const & p)
    {
      para.insert(p);
    }
    void insert(std::vector<string> & para, std::string const & p)
    {
      para.push_back(p);
    }
    //...
    
    // In der main steht dann beispielsweise:
    int main()
    {
      myContainer mycon;
      insert(mycon, "value");
    }
    

    Auch unter Java, C#... würde ich unnötige Typabfragen vermeiden, wenn sie nicht nötig sind. Wobei man grundsätzlich in jeder Sprache anders programmiert...



  • Kommt das letztendlich nicht aufs selbe raus?

    std::vector<std::string> vec;
    //entweder
    vec.push_back("eins");
    //oder
    vec.insert(vec.end(),"zwei");
    

    Also braucht man sich doch nichtmal um eine Unterscheidung kümmern, sondern kann gleich insert verwenden.
    Da insert("zwei") auf einem set vermutlich äquivalent zu insert(begin(),"zwei") ist, wird es wohl auch keinen Unterschied machen wenn man insert(end(),"zwei") aufruft (performance betreffend -> richtige stelle sollte ebenso schnell gefunden werden)



  • inter2k3 schrieb:

    Kommt das letztendlich nicht aufs selbe raus?

    std::vector<std::string> vec;
    //entweder
    vec.push_back("eins");
    //oder
    vec.insert(vec.end(),"zwei");
    

    Also braucht man sich doch nichtmal um eine Unterscheidung kümmern, sondern kann gleich insert verwenden.
    Da insert("zwei") auf einem set vermutlich äquivalent zu insert(begin(),"zwei") ist, wird es wohl auch keinen Unterschied machen wenn man insert(end(),"zwei") aufruft (performance betreffend -> richtige stelle sollte ebenso schnell gefunden werden)

    Ich würde sagen, du befindest dich damit auf ganz dünnem Eis 🙄
    http://www.cplusplus.com/reference/stl/set/insert/

    Notice that this does not force the new element to be in that position within the set container (elements in a set always follow a specific ordering), but this is actually an indication of a possible insertion position in the container that, if set to the element that precedes the actual location where the element is inserted, makes for a very efficient insertion operation.



  • Kann ich nicht erkennen, dass ich da auf dünnem Eis bin.
    Wie dort steht ist es nur ein Hinweis wo er es einfügen könnte um ggf. schneller die endgültige Position zu ermitteln.



  • inter2k3 schrieb:

    Kann ich nicht erkennen, dass ich da auf dünnem Eis bin.
    Wie dort steht ist es nur ein Hinweis wo er es einfügen könnte um ggf. schneller die endgültige Position zu ermitteln.

    Ich nehm alles zurück... 😞
    Ich hab wohl nicht genau hingekuckt und bin wohl deswegen davon ausgegangen,
    dass er zwangsläufig versucht in diesem Teilbaum (Ich weiß: set ist laut Standard
    nicht zwangsläufig nen Baum) hinzuzufügen und du könntest die order zerstören.

    EDIT: Aber inperformanter bist du wohl, da du ja nen zusätzlichen Vergleich brauchst,
    bist du an der Wurzel anfängst zu suchen 😉



  • Jay1980 schrieb:

    Introspektion ist ja in C++ nicht so direkt machbar wie etwa in Java. Ich will aber gerne nur an einer Stelle festlegen ob mein Programm ein set<string> oder einen vector<string> nimmt - das ist aber blöd, denn je nachdem was zur Laufzeit genommen wird, muss die Methoden entweder push_back() oder insert() heissen. Wie macht man das dann normalerweise?

    Warum zur Laufzeit bestimmen, was schon zur Kompilierzeit bekannt ist?



  • Also ich würde hier auch überladene Hilfsfunktionen verwenden, wie in den Beispielen von XSpille und asc.


Anmelden zum Antworten