primitives instanceof



  • Tachyon schrieb:

    Ulkig. Bei der Anforderung "...in dem ich beliebige Klassen und auch primitive Datentypen speichern kann..." finde ich diese Feststellung nicht sehr naheliegend.

    Selbst dann wird wahrscheinlich eine art "ClassType" ausreichen 😉

    Tachyon schrieb:

    Wenn er es noch mit "Type Erasure" versuchen soll, was versucht er dann Deiner Meinung nach jetzt?

    Was er jetzt versucht? Mit boost::any was zusammenzufrickeln. Du weißt doch selbst, dass sich da schöneres machen ließe.

    boost::any ist halt quasi immer der falsche Weg. Außerdem kann ich bei eigener Type-Erasure viel einfacher (und performanter durch einen dynamic_cast weniger) definieren "A wird durch Verfahren X nach B gecastet". Der Ansatz von wxSkip z.B ist recht nett.



  • Bei einer Scriptsprache würde ich in Erwägung ziehen, einfach eine Union zu verwenden, um weiteres, unnötiges aber teures, Allozieren von Speicher zu vermeiden.
    Zb boost::any wird doch eh alles auf den Heap legen müssen?



  • Ethon schrieb:

    Bei einer Scriptsprache würde ich in Erwägung ziehen, einfach eine Union zu verwenden, um weiteres, unnötiges aber teures, Allozieren von Speicher zu vermeiden.

    Nichts anderes ist boost::variant. Um genau zu sein ist es intern ein char[], in dem die Objekte mit placement-new erstellt werden.

    Ethon schrieb:

    Zb boost::any wird doch eh alles auf den Heap legen müssen?

    Genau. any hat ein template, das auf den gespeicherten Typ parametrisiert ist. Dieses template ist von einer gemeinsamen Basisklasse abgeleitet. Intern wird hier im Konstruktor, op =, .... ein wrapper<T> auf dem Heap erstellt und als wrapper_base* gespeichert.
    any_cast ist nichts anderes als ein gewrappter dynamic_cast von wrapper_base* zu wrapper<T>*.



  • Accountlose Wurst schrieb:

    ich brauche einen Typ, in dem ich beliebige Klassen und auch primitive Datentypen speichern kann. [...]
    Benötigt wird das Ganze für eine dynamisch typisierte Skriptsprache. [...]

    Das glaube ich Dir nicht, dass so'was benötigt wird. Nicht für beliebig viele Typen. Für eine kleine, fixe Anzahl von Typen, ja. Aber nicht für beliebig viele. Mehrere Script-Klassentypen würde man mit einem einzigen C++-Typ erschlagen, dessen Objekte die Script-Klassentypen beschreiben:

    struct script_class_object;
    
    struct script_immutable_string
    {
       long length;
       const char* data() const {return reinterpret_cast<const char*>(this+1);}
    };
    
    shared_ptr<const script_immutable_string> make_string(char const* p)
    {
      ...magic...
    }
    
    typedef boost::variant<
                long,
                double,
                shared_ptr<const script_immutable_string>,
                shared_ptr<script_class_object>
            > script_variable;
    
    struct script_class_type_descriptor
    {
      string classname;
      shared_ptr<script_class_type_descriptor> base_type;
      ...
    };
    
    struct script_class_object;
    {
      shared_ptr<script_class_type_descriptor> class_type;
      map<string, script_variable> members;
    };
    

    (oder so ähnlich)



  • Hat zwar jetzt nichts direkt mit dem Thema zu tun, aber wie ist denn boost::any implementiert? Braucht das die RTTI? Ich frage, weil mir grade eine Methode eingefallen ist, man das vielleicht ohne RTTI implementieren könnte (und ich wissen will, obs eine Ausarbeitung wert ist)



  • - Hier stand Mist, natürlich brauchen virtuelle Funktionen auch RTTI -

    Edit: Mal ne beispielhafte, wenn auch nicht komplette Implementierung...

    class any
    {
    	struct any_wrapper_base
    	{
    		virtual any_wrapper_base* clone() const = 0;
    		virtual ~any_wrapper_base() {}
    	};
    
    	template <typename T>
    	struct any_wrapper : any_wrapper_base
    	{
    		T obj;
    
    		any_wrapper(const T& obj) : obj(obj) {}
    		virtual any_wrapper* clone() const { return new any_wrapper(obj); }
    	};
    
    	any_wrapper_base* wrapper;
    
    public:
    	any() : wrapper(0) {}
    	~any() { delete wrapper; }
    
    	template <typename T>
    	any(const T& value) : wrapper(new any_wrapper<T>(value)) {}
    
    	template <typename T>
    	any& operator = (const T& value)
    	{
    		any_wrapper_base* ptr = new any_wrapper<T>(value);
    		delete wrapper;
    		wrappter = ptr;
    		return *this;
    	}
    
    	any(const any& other) : wrapper(other.wrapper->clone()) {}
    
    	any& operator = (const any& other)
    	{
    		any_wrapper_base* ptr = other.wrapper->clone();
    		delete wrapper;
    		wrapper = ptr;
    		return *this;
    	}
    
    	template <typename T>
    	friend T any_cast(any& a)
    	{
    		return dynamic_cast<any_wrapper<T>&>(*wrapper).obj;
    	}
    };
    


  • Dann ist sowas also nicht möglich, d.h. schmeist ne exception?

    int main()
    {
    	boost::any asd = (int)5;
    	long value = boost::any_cast< long >( asd );
    	std::cout<< value<<std::endl;
    	std::cin.get();
    }
    


  • So ist es.



  • Dann bin ich mir ziemlich sicher dass es eine Möglichkeit gibt, das ohne dynamic_cast zu bauen.

    EDIT: Hab jetzt grade wohl nicht genug Zeit, aber bis heute abend werd ich mal schauen ob ichs hinbekomm



  • So, hier mal ne Demo, bei der man aber meinen Ansatz denk ich erkennt:

    #include <iostream>
    
    unsigned int nextID()
    {
    	static unsigned int current_id = 0 ;
    
    	++current_id;
    	return current_id;
    }
    
    template< class Type >
    unsigned int typeID()
    {
    	static unsigned int id = nextID();
    
    	return id;
    }
    
    class AnyTypeWrapper
    {
    public:
    	virtual ~AnyTypeWrapper()
    	{
    	}
    	virtual AnyTypeWrapper *clone() = 0;
    	virtual void *getObjectMemory() = 0;
    };
    
    template< class T >
    class TypeWrapper
    	: public AnyTypeWrapper
    {
    private:
    	T obj;
    public:
    	TypeWrapper( const T &Object )
    		: obj( Object )
    	{
    	}
    	AnyTypeWrapper *clone()
    	{
    		return new TypeWrapper< T >( obj );
    	}
    	void *getObjectMemory()
    	{
    		return &obj;
    	}
    };
    
    class any
    {
    private:
    	unsigned int id;
    	AnyTypeWrapper *wrapper;
    public:
    	any()
    	{
    		id = 0;
    		wrapper = 0;
    	}
    	template< class T >
    	any( const T &Object )
    	{
    		id = typeID< T >();
    		wrapper = new TypeWrapper< T >( Object );
    	}
    	~any()
    	{
    		delete wrapper;
    	}
    	template< class T >
    	friend T any_cast( any &any_instance )
    	{
    		if( typeID< T >() == any_instance.id )
    		{
    			return *reinterpret_cast< T* >( any_instance.wrapper->getObjectMemory() );
    		}
    		else
    		{
    			throw 1;
    		}
    	}
    };
    
    int main()
    {
    	any anything = (int) 5;
    	try
    	{
    		any_cast< int >( anything );
    		std::cout<< "int" <<std::endl;
    	}catch(...)
    	{
    		std::cout<< "not int" <<std::endl;
    	}
    
    	try
    	{
    		any_cast< long >( anything );
    		std::cout<< "long" <<std::endl;
    	}catch(...)
    	{
    		std::cout<< "not long" <<std::endl;
    	}
    
    	std::cin.get();
    }
    


  • Abgesehen davon, dass du hier den falschen Cast verwendest, siehts richtig aus.



  • welcher falsche cast?



  • Da gehört ein static_cast hin statt reinterpret_cast 😉



  • Das ist aber ein void*, den ich da caste.



  • Von und zu void* (wobei zu void* auch implizit geht) wird mit static_cast gecastet - richtig :p



  • GorbGorb schrieb:

    Das ist aber ein void*, den ich da caste.

    static_cast castet void* von und zu anderen Zeigertypen. 😉



  • 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^^.


Anmelden zum Antworten