primitives instanceof
-
Hallo,
ich brauche einen Typ, in dem ich beliebige Klassen und auch primitive Datentypen speichern kann.
Zuerst habe ich an void* gedacht, allerdings muss ich später auch wieder auf die Daten zugreifen können.
Für Klassen gibt es wohl eine Art "instanceof" mit typeid und einer virtuellen Methode. Das ist aber eben mit den primitiven Typen nicht möglich.Ich habs schon mit Macros und anderem Zeug versucht, nichts hat bisher geklappt. Gibt es da eine Möglichkeit?
Benötigt wird das Ganze für eine dynamisch typisierte Skriptsprache. Die Variablen sollten dann eben per std::map<std::string, void*> (Name, Inhalt) gespeichert werden.
-
Eventuell wäre boost::any etwas für dich.
-
Eher boost::variant würde ich sagen. (Visitor Pattern und so)
-
@ FreakY<3Cpp: Sieht auf jeden Fall besser aus, als void*

Ungefähr regeln würde ich das so:
class Variable { public: enum Data_Type {NO_TYPE, CHAR_TYPE, INT_TYPE, DOUBLE_TYPE, STRING_TYPE}; Variable(); template<typename T> Variable(T data); Data_Type get_type(); char get_char(); int get_int(); double get_double(); string get_string(); private: boost::any data; }; Variable::Variable() {} template <typename T> Variable::Variable(T data) { this->data = data; } Variable::Data_Type Variable::get_type() { if (data.type() == typeid(char)) return CHAR_TYPE; if (data.type() == typeid(int)) return INT_TYPE; if (data.type() == typeid(double)) return DOUBLE_TYPE; if (data.type() == typeid(string)) return STRING_TYPE; return NO_TYPE; } char Variable::get_char() { return boost::any_cast<char>(data); } int Variable::get_int() { return boost::any_cast<int>(data); } double Variable::get_double() { return boost::any_cast<double>(data); } string Variable::get_string() { return boost::any_cast<string>(data); }int main() { Variable var(string("Hallo Welt")); switch (var.get_type()) { case Variable::CHAR_TYPE: cout << var.get_char() << endl; break; case Variable::INT_TYPE: cout << var.get_int() << endl; break; case Variable::DOUBLE_TYPE: cout << var.get_double() << endl; break; case Variable::STRING_TYPE: cout << var.get_string() << endl; break; default: cout << "No Type." << endl; } return 0; }Scheint mir aber immer noch etwas umständlich zu sein. Gibt es da einen kürzeren Weg?
@ 314159265358979: Ich habe mir beides angesehen (und hoffentlich auch verstanden :D), aber keine Idee, wie das mein Problem löst.
Könntest du da mehr erläutern?
-
Accountlose Wurst schrieb:
ich brauche einen Typ, in dem ich beliebige Klassen und auch primitive Datentypen speichern kann.
Nö.
-
Du willst einen Typen aus einer bekannten Menge an Typen speichern. Dafür ist boost::variant gedacht. boost::variant ist nichts anderes als eine verbesserte union - sie enthält immer nur Typ A oder B oder C oder ...
Im Unterschied zu einer richtigen union kannst du in eine boost::variant auch non-PODs werfen - also Dinge wie std::vector. Die Verwendung dürftest du dir aus der Doku zusammensuchen können - ich müsste dasselbe tun, da ich damit schon länger nichts gemacht habe
Edit: Vielleicht tuts aber auch schon der "klassische" Ansatz über Type Erasure (nächstes Schlüsselwort
)
-
314159265358979 schrieb:
Du willst einen Typen aus einer bekannten Menge an Typen speichern.
Ulkig. Bei der Anforderung "...in dem ich beliebige Klassen und auch primitive Datentypen speichern kann..." finde ich diese Feststellung nicht sehr naheliegend.
Wenn er es noch mit "Type Erasure" versuchen soll, was versucht er dann Deiner Meinung nach jetzt?
-
Tachyon schrieb:
314159265358979 schrieb:
Du willst einen Typen aus einer bekannten Menge an Typen speichern.
Ulkig. Bei der Anforderung "...in dem ich beliebige Klassen und auch primitive Datentypen speichern kann..." finde ich diese Feststellung nicht sehr naheliegend.
Er schreibt, dass er das für eine dynamische Scriptsprache braucht. In der Scriptsprache gibt es sicher nur eine begrenzte Anzahl an Typen, die auch als verschiedene Typen im C++-Interpreter repräsentiert werden. Seine Beispiele legen das auch nahe. Wenn sowieso nur auf char, int, double, string geswitcht wird, wozu braucht man dann die Möglichkeit, in den Container noch andere als diese Datentypen hineinzupacken?
-
Falls hier von einem Interpreter o.ä. für diese Scriptsprache die Rede ist, würde ich auch einmal ein anderes Konzept ins Auge fassen: (aka Composite)
class Type { public: virtual ~Type() = 0; virtual shared_ptr<Type> CallMemberFunction(std::string const &name) = 0; virtual bool IsVoidType(); }; class VoidType{...}; class StringType{...}; class IntType{...}; class StructType { std::vector<shared_ptr<Type> > subtypes; ... }; void perform_function(std::string const &name, std::vector<shared_ptr<Type> > const &arguments); //evtl. auch mit Dispatching oder so etwas
-
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?