primitives instanceof



  • Wieder was gelernt 😃
    Was kostet denn eigentlich ein dynamic_cast im Vergleich zu einem if? (bzw. lohnt sich meine Version im allgemeinen?)
    EDIT: Meine natürlich performance mäßig



  • @GorbGorb:
    Ich habe vor einiger Zeit etwas ziemlich ähnliches geschrieben (habe mir deine Lösung jetzt nicht im Detail angeschaut):

    extern int t_id_counter;
    
    template<typename T> int GetTypeID()
    {
        static int i = -1;
        if(i == -1)
        {
            i = t_id_counter;
            ++t_id_counter;
        }
        return i;
    }
    
    class PtrManagerBase
    {
        PtrManagerBase(PtrManagerBase const &);
        PtrManagerBase &operator=(PtrManagerBase const &);
    
        public:
        PtrManagerBase(){}
        virtual ~PtrManagerBase(){}
        virtual void *Get() = 0;
        virtual PtrManagerBase *Copy() = 0;
    };
    
    template<typename T> class PtrManager : public PtrManagerBase
    {
        T *ptr;
    
        PtrManager(PtrManager<T> const &);
        PtrManagerBase &operator=(PtrManager<T> const &);
    
        public:
        PtrManager(T *in) : ptr(in){}
        virtual ~PtrManager()
        {
            delete ptr;
        }
    
        virtual void *Get()
        {
            return static_cast<void *>(ptr);
        }
    
        virtual PtrManagerBase *Copy()
        {
            return new PtrManager<T>(new T(*ptr));
        }
    };
    
    class AnyType
    {
        PtrManagerBase *ptr_mgr;
        int t_id;
    
        public:
        AnyType();
        template<typename T> AnyType(T const &in) : ptr_mgr(new PtrManager<T>(new T(in))), t_id(GetTypeID<T>())
        {
        }
    
        AnyType(AnyType const &in);
        AnyType &operator=(AnyType const &in);
        virtual ~AnyType();
    
        template<typename T> void Set(T const &in)
        {
            delete ptr_mgr;
            ptr_mgr = new PtrManager<T>(new T(in));
            t_id = GetTypeID<T>();
        }
    
        template<typename T> bool Is()
        {
            return t_id == GetTypeID<T>();
        }
    
        template<typename T> T Get()
        {
            return *(static_cast<T *>(ptr_mgr->Get()));
        }
    };
    
    //und das in die C++-Datei
    int t_id_counter = 0;
    
    AnyType::AnyType() : ptr_mgr(NULL), t_id(-1)
    {
    }
    
    AnyType::AnyType(AnyType const &in) : ptr_mgr(NULL), t_id(in.t_id)
    {
        if(in.ptr_mgr != NULL)
        {
            ptr_mgr = in.ptr_mgr->Copy();
        }
    }
    
    AnyType &AnyType::operator=(AnyType const &in)
    {
        delete ptr_mgr;
        if(in.ptr_mgr == NULL) ptr_mgr = NULL;
        else ptr_mgr = in.ptr_mgr->Copy();
        t_id = in.t_id;
    
        return *this;
    }
    
    AnyType::~AnyType()
    {
        delete ptr_mgr;
    }
    


  • Schon wieder so ein reinterpret_cast ala void* :p



  • Naja, das Resultat ist ja das Gleiche. Und irgendwie macht es ja auch (in natürlicher Sprache) Sinn, dass man "irgendetwas" neu interpretiert als "XY".

    Ich hab mir mal die source zu boost::any angeschaut, und es sieht so aus, als ob die sich in any_cast per virtual function eine typeid von *content holen, die sie dann mit operator== mit typeid( ArgumentType ) vergleichen. Ich bin mir sehr sicher, dass das mit meiner Version deutlich schneller geht (hab grade im gcc getestet, und so wie es aussieht braucht eine typeid-comparison etwa 5x so lang wie ein int Vergleich). Vielleicht arbeite ich das noch weiter aus und schicks den boost-Leuten mal, hoffentlich wird was draus. Obwohl es natürlich sehr unwahrscheinlich ist, dass jemand der mit boost::any arbeitet groß auf performance achtet^^.



  • GorbGorb schrieb:

    Vielleicht arbeite ich das noch weiter aus und schicks den boost-Leuten mal, hoffentlich wird was draus. Obwohl es natürlich sehr unwahrscheinlich ist, dass jemand der mit boost::any arbeitet groß auf performance achtet^^.

    Ich habe mich das auch gefragt, glaube aber weniger, dass die boost-Leute diese doch noch einigermaßen naheliegende Lösung (immerhin sind wir beide draufgekommen) übersehen habe.

    314159265358979 schrieb:

    Schon wieder so ein reinterpret_cast ala void* :p

    Mist, den anderen hatte ich schon abgeändert. So, jetzt ist es aber editiert.



  • GorbGorb schrieb:

    Naja, das Resultat ist ja das Gleiche.

    Da wäre ich mir nicht so sicher. Wenn static_cast geht, ziehe es vor.

    GorbGorb schrieb:

    Ich bin mir sehr sicher, dass das mit meiner Version deutlich schneller geht (hab grade im gcc getestet, und so wie es aussieht braucht eine typeid-comparison etwa 5x so lang wie ein int Vergleich). Vielleicht arbeite ich das noch weiter aus und schicks den boost-Leuten mal, hoffentlich wird was draus.

    Vielleicht solltest du zuerst ein paar Bugs beseitigen (Kopiersemantik). Und etwas ausführlicher und in verschiedenen Situationen auf verschiedenen Compilern testen. Und dann gibts noch heimtückischere Fallen, z.B. die fehlende Threadsicherheit beim Zugriff auf static -Objekte. Generell würde ich vielleicht eher zuerst in der Boost-Mailingliste eine Diskussion starten 😉

    Ausserdem könntest du möglicherweise die Performance weiter optimieren, indem du auf die AnyTypeWrapper -Klasse verzichtest und stattdessen direkt void* und Funktionszeiger verwendest. Macht auch den Code kürzer.

    Aber die Idee mit automatisch vergebenen Integer-IDs für Typen ist echt gut. Ich muss damit unbedingt mal herumexperimentieren. Ich habe nämlich das Problem, dass meine Dynamic-Dispatchers elend langsam sind, und der Grossteil der Zeit bei std::type_info -Vergleichen verloren geht.



  • Nexus schrieb:

    Ich habe nämlich das Problem, dass meine Dynamic-Dispatchers elend langsam sind, und der Grossteil der Zeit bei std::type_info -Vergleichen verloren geht.

    Alexandrescu hat einen O(1) Dispatcher, wenn ich mich recht erinnere 🙂



  • 314159265358979 schrieb:

    Alexandrescu hat einen O(1) Dispatcher, wenn ich mich recht erinnere 🙂

    Ja, aber der ist intrusiv. So einen baue ich vielleicht auch eines Tages, aber ich brauche nebenbei auch eine Version, die ohne Veränderung bestehender Klassen auskommt.

    Aber ich sehe gerade, eins habe ich nicht bedacht: Um eine virtuelle Funktion oder RTTI komme ich nicht herum, falls ich zur Laufzeit die Typ-ID eines Objekts herausfinden will. Die Integer-Lösung bringt mir hier also nicht allzu viel. Aber vielleicht anderswo... 😉



  • Klassen in der Skriptsprache sind nicht geplant. Ich meinte im Startpost, dass ich beliebige C++-Klassen im Container speichern können will.
    Es werden also nur feste Typen erlaubt sein.

    @ 314159265358979:

    Type Erasure

    Wo ist dabei der Vorteil gegenüber boost::any?

    boost::variadic

    Ich habe die which()-Methode übersehen. Mit der wäre das natürlich möglich.
    Anderes Problem: Dieser Artikel sagt mir, dass Visual Studio noch keine Variadic Templates unterstüzt.
    Außerdem sehe ich immer noch nicht den Zusammenhang zum Visitor Pattern.

    @ wxSkip: Ich verstehe nicht, was das mit dem Problem zu tun hat? Oder beziehst du das auf Klassen + Member?

    @ Ethon: Unions erlauben aber keine nicht-PODs.

    @ krümelkacker: Ich habe nie von beliebig vielen Typen geredet, sondern nur von beliebigen Typen 😉



  • Zum Visitor Pattern: Boost lässt dich mit apply_visitor einen Visitor auf die Variant anwenden. Nehmen wir an, in deiner Sprache steht der Ausdruck "a + b". Je nachdem, welchen Typ deine Variablen haben, kann das verschiedenes bedeuten. Zuerst stellst du fest, dass a z.B. ein string ist. Jetzt lässt du einen visitor<string> über b laufen. Ist b ebenfalls ein string, dann kannst du diesen direkt zurückgeben. Ist b ein int, dann kannst du ihn implizit konvertieren und zurückgeben. Ist b etwas anderes, könntest du eine Exception werfen. Mit dem Visitor pattern könnte man hier also Konvertierungen umsetzen.

    Was Type Erasure angeht hatte ich ähnliche Gedanken, aber mitterweile denke ich, dass boost::variant am geeignetsten ist.



  • @Wurst: Ich weiß immer noch nicht genau, was du machen willst. Willst du für die Typen in deiner Skriptsprache genau die gleichen C++-Typen verwenden. Oder willst du die unbekannten C++-Typen nur gleichartig in deinem Interpreter verwenden? Es ist ein Zeichen von gutem Design, wenn du Polymorphie Typecasts und Identifizierungen vorziehst. Wenn du mit variant/any/... einfach nur Variablen repräsentieren willst, kannst du das mit Instanzen eigener Typklassen machen, die von einer (abstrakten) Basisklasse abgeleitet werden. Dann kannst du in vielen Fällen die Member der Klassen selbst die Operation spezifisch für den bestimmten Typ durchführen lassen.



  • @ 314159265358979: Macht Sinn. Ich hatte vor, Funktionen zum Konvertieren zu schreiben, aber so ist es einfacher.

    @ wxSkip: Ich möchte in meiner Skriptsprache die Typen bool, int, double, char, string und eventuell noch andere haben. Diese sollen vom Interpreter mit den gleichnamigen C++-Typen gespeichert werden.
    Da ich noch nicht so weit mit der Planung des Codes bin, bin ich mir nicht sicher, ob mit Polymorphie alles zu regeln wäre.

    Da MSVC keine Variadic Templates unterstützt wird es wohl dennoch auf boost::any oder Polymorphie + enum mit dem Variablentyp hinauslaufen. Außer, Visual Studio 2011 kommt in den nächsten paar Wochen :p

    Weitere Vorschläge sehe ich natürlich gern, aber trotzdem bedanke ich mich schon für die bereits aufgekommenen.



  • Auch wenn das jetzt sicher nicht das beste Design ist, manchmal ist einfach besser (wurde hier ja auch schonmal vorgeschlagen):

    enum Type
    {
      TYPE_CHAR,
      TYPE_INT,
      ...
    };
    
    struct Variable
    {
      Type type;
      union
      {
        char char_value;
        int int_value;
        ...
      } value;
    };
    


  • Wurst schrieb:

    Ich möchte in meiner Skriptsprache die Typen bool, int, double, char, string und eventuell noch andere haben.

    Das schreit nach Polymorphie, denn mit einer enum/union/any-Konstruktion darfst du jedes Mal alle Casting-Versuche um einen Typ ergänzen. Und die Möglichkeit eines Einsatzes von Polymorphie deutet auch auf einen logischen Sprachaufbau deiner Scriptsprache hin.



  • Wurst schrieb:

    Klassen in der Skriptsprache sind nicht geplant. Ich meinte im Startpost, dass ich beliebige C++-Klassen im Container speichern können will.

    Du meinst sicherlich "Objekte von beliebigen Typen". Aber wozu?

    Wurst schrieb:

    @ krümelkacker: Ich habe nie von beliebig vielen Typen geredet, sondern nur von beliebigen Typen 😉

    Da man Typen nicht "speichert", sondern höchstens "unterstützt", macht das keinen Unterschied.



  • @ dot:
    union will aber keine strings haben.

    @ wxSkip:
    Logisch? Eigenlich nicht.
    Welche Typen ich letztendlich einbauen werde, werde ich mir überlegen, bevor ich mit dem Programmieren anfange. "ergänzen" muss ich also nicht 🙂

    @ krümelkacker:

    Du meinst sicherlich "Objekte von beliebigen Typen". Aber wozu?

    Ja, stimmt, ich meinte Objekte. Wozu, steht doch im Startpost.

    Da man Typen nicht "speichert", sondern höchstens "unterstützt", macht das keinen Unterschied.

    Darum nennt man ihn also den "Arbeitsunterstützer"? 😉



  • Wurst schrieb:

    @ dot:
    union will aber keine strings haben.

    Aber std::string*. Und wie schon erwähnt kannst du in eine variant std::string reinwerfen.



  • Wurst schrieb:

    @ dot:
    union will aber keine strings haben.

    In C++0x wär das kein Problem 😉



  • Wurst schrieb:

    Da man Typen nicht "speichert", sondern höchstens "unterstützt", macht das keinen Unterschied.

    Darum nennt man ihn also den "Arbeitsunterstützer"? 😉

    Du verwechselst wohl wieder Typen und Objekte.



  • Wenn es sich um eine dynamische typisierte Skriptsprache handelt, warum schaust du dir nicht mal eine Implementation an? Zu empfehlen sind Lua oder Scheme in verschiedenen Versionen. Fuer gewoehnlich wird mit union + type tag gearbeitet. Darueber hinaus gibt es kleinere Optimierungen bezueglich einfacher Datentypen wie Byte, Double oder Integer.


Anmelden zum Antworten