Unser neuestes Mitglied: miknevc1

88 Besucher derzeit online [ Zeigen ]

Startseite | Werbefreies Forum: Jetzt Mitglied werden! | Login | Team | Suchen
Statistik | Hilfe / FAQ | Regeln
Willkommen auf dem xpBulletin Board, Gast.
Du bist nicht registriert oder eingeloggt.
Dein Letzter Besuch war am: 23.09.2014, 10:18 Uhr.
[ Alles als gelesen markieren ]
xpBulletin Board » Programmierung » C und C++ - Forum » Variierender Datentyp
Benutzer im Forum aktiv: Keine

[ Neue Antwort ]


Autor
Thema: Variierender Datentyp

 

Seiten (2): [1], 2 »

Neuer Beitrag 26.01.2008, 19:46
 Variierender Datentyp

#1 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich
Eine meiner Klassen enthält unter anderem unbekannt viele Variablen unbekannter Datentypen. Wie kann ich das in c++ mit sauberer Objektorientierung erfassen? Ich habe schon halbwegs funktionierende aber grauenvolle, unschöne Varianten ausprobiert:

Versuch 1:
Ich benutze eine dynamische Liste von void-Zeigern, die auf die verschiedenen Daten verweisen. Außerdem habe ich eine ebensolange Liste, die den jeweiligen Typ speichert.
Code:

enum Typ { INT, BOOL, STRING, ... };
list<void*> daten;
list<Typ> typen;


Nachteile: Ich muss für jeden Eintrag den jeweiligen Datentyp mitspeichern und ständig in endlosen switch-Anweisungen umständlich casten. Außerdem muss ich für jeden hinzukommenden Datentyp die enum-Liste erweitern und meinen ganzen Code nach switch-Anweisungen durchsuchen, die angepasst werden müssen.

Versuch 2:
Ein Spuck aus Vererbung, Polymorphie und Templates. *grusel* Ich gehe lieber nicht weiter darauf ein.

Versuch 3:
Ich benutze eine dynamische liste von Unions, wobei die Unions alle gängigen Datentypen aufnehmen können. Damit hab ich die selben Nachteile wie in Versuch 1, kann jedoch auf die vielen Typumwandlungen verzichten.
Code:

union data {
  int intData;
  bool boolData;
  string* stringData;
  ...
};
...
list<data> daten;



Wie gesagt haben alle Varianten funktioniert; jedoch nur durch massive, mir das c++-Herz bluten lassende Unterwanderungen der Objektorientierung. Gibt es eine elegantere Variante, mit unbekannten Datentypen umzugehen?

gruß Marcel
 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 26.01.2008, 19:46
 Werbung

AdBot
Mister Ad

 
Neuer Beitrag 27.01.2008, 09:42
 Re: Variierender Datentyp

#2 | Zitieren |

Darii ist Offline Darii
Ancient Hacker



Alter: 29
Geschlecht: Männlich
koril-k schrieb:
Wie gesagt haben alle Varianten funktioniert; jedoch nur durch massive, mir das c++-Herz bluten lassende Unterwanderungen der Objektorientierung. Gibt es eine elegantere Variante, mit unbekannten Datentypen umzugehen?

Soweit ich weiß nicht. C++ ist nunmal eine statisch typisierte Sprache damit muss man leben oder eine andere Sprache nehmen(als (gleichwertige) Alternative fällt mir da nur Objective C ein, aber außerhalb von Mac hat man da eher schlechte Karten mit).
 Beiträge: 1.042 | Punkte: 1.155 | Wohnort: Hannover(Region) | Registriert seit: 4517 Tagen (May 2002)
 
Neuer Beitrag 27.01.2008, 11:19
 Re: Variierender Datentyp

#3 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich

Themenstarter
Ich habe nochmal meine 2. Variante versucht und bin zu einem recht guten Ergebnis gekommen. Was haltet ihr davon? Ist natürlich nur ein grober Entwurf.

Code:

#include <iostream>
#include <cassert>
using namespace std;

class Data
{
    // Dummyklasse um Polymorphie benutzen zu können.
    public:
        Data() {}
        virtual ~Data() {}
};

template <class T>
class BelData : public Data
{
    public: T inhalt;
    // Natürlich schöner gekapselt und alles.
};

// Datentupel darstellen.
class Datentupel
{
    private:

    Data** daten;
    int anzahl;

    public:

    Datentupel( int anz ): daten( new Data* [anz] ), anzahl(anz) {
        for( int i=0; i<anz; ++i ) daten[ i ] = NULL;
    }

    ~Datentupel() {
        for( int i=0; i<anzahl; ++i ) delete daten[ i ];
        delete[] daten;
    }

    // Datentupel füllen (Änderungen sind in diesem Entwurf nicht möglich).
    template <class T>
    void fillData( const T& r, int nr ) {
        assert( daten[nr] == NULL );
        daten[nr] = new BelData<T>;
        dynamic_cast< BelData<T>* >(daten[nr])->inhalt = r;
    }

    // Daten abfragen.
    template <class T>
    const T& getData(int nr, const T& ) const {
        assert( daten[nr] != NULL );
        return dynamic_cast< BelData<T>& >(*daten[nr]).inhalt;
    }
};


int main()
{
    Datentupel infos(3);
    infos.fillData(2007,0);
    infos.fillData(12.3F,1);
    infos.fillData(true,2);
    cout << infos.getData(1,float()) << endl;
    // Gibt "12.3" aus.

    return 0;
}



Edit: Auszusetzen ist noch die Angabe des Datentyps in der getData-Funktion, da dieser ja schon durch den ersten Parameter bestimmt ist.

Beitrag editiert von koril-k am 27.01.2008, 14:16
 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 27.01.2008, 11:19
 Werbung

AdBot
Mister Ad

 
Neuer Beitrag 27.01.2008, 16:25
 Re: Variierender Datentyp

#4 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich

Themenstarter
Ich habe die Klasse vernünftig ausgearbeitet.

Code:

class Datentupel
{
    public:

        // Constructors und Zuweisung.

        Datentupel( int anz ): daten( new Data* [anz] ), anzahl(anz) {
            for( int i=0; i<anz; ++i ) daten[ i ] = NULL;
        }

        Datentupel( const Datentupel& r )
        : daten( new Data* [r.anzahl] ), anzahl(r.anzahl) {
            for( int i=0; i<anzahl; ++i )
                 daten[ i ] = r.daten[ i ]->copyMe();
        }

        ~Datentupel() {
            for( int i=0; i<anzahl; ++i ) delete daten[ i ];
            delete[] daten;
        }

        void operator = ( const Datentupel& r ) {
            for( int i=0; i<anzahl; ++i ) delete daten[ i ];
            delete[] daten;
            anzahl = r.anzahl;
            daten = new Data* [anzahl];
            for( int i=0; i<anzahl; ++i )
                daten[ i ] = r.daten[ i ]->copyMe();
        }


        // Vergleiche.

        bool operator == ( const Datentupel& r ) {
            if( anzahl != r.anzahl ) return false;
            for( int i=0; i<anzahl; ++i ) {
                if( *daten[ i ] != *(r.daten[ i ]) ) return false;
            }
            return true;
        }

        bool operator != ( const Datentupel& r ) {
            return !(*this == r);
        }

        // Eingang eines Datentupels ändern.
        template <class T>
        void setData( const T& r, int nr ) {
            assert( nr>=0 && nr<anzahl );
            delete daten[nr];
            daten[nr] = new BelData<T>;
            dynamic_cast< BelData<T>* >(daten[nr])->inhalt = r;
        }

        // Daten abfragen.
        template <class T>
        const T& getData(const T&, int nr ) const {
            assert( nr>=0 && nr<anzahl );
            assert( daten[nr] != NULL );
            BelData<T>* ptr = dynamic_cast< BelData<T>* >(daten[nr]);
            assert( ptr != NULL );
            return ptr->inhalt;
        }

    private:

        // Abstrakte Dummyklasse um Polymorphie benutzen zu können.
        struct Data {
            Data() {}
            virtual ~Data() {}
            virtual Data* copyMe() const = 0;
            virtual bool operator != ( const Data& ) const = 0;
        };

        // Stellt beliebigen Datentyp dar.
        template <class T>
        struct BelData : public Data {
            T inhalt;

            // Dummycunstructors empfangen den virtuellen Aufruf
            // der Konstruktoren aus der Data-Klasse.
            BelData() {}
            ~BelData() {}

            // Erstellt eine Kopie der eigenen Instanz.
            Data* copyMe() const {
                BelData<T>* present = new BelData<T>;
                present->inhalt = inhalt;
                return present;
            }

            // Vergleich.
            bool operator != ( const Data& r ) const {
                const BelData<T>* refPtr = dynamic_cast<const BelData<T>*>(&r);
                if( refPtr == NULL ) return true;
                return (inhalt != refPtr->inhalt);
            }
        };

    private:

        // Datenarray und dessen Länge.
        Data** daten;
        int anzahl;
};

 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 27.01.2008, 23:12
 Re: Variierender Datentyp

#5 | Zitieren |

bg ist Offline bg

Operator
Zen Master Foo


Alter: 33
Geschlecht: Männlich
Dein Einfall mit Data und BelData ist schon einmal sehr gut. Ich gehe hier mal noch einen Schritt weiter und Kapsle das ganze in eine Klasse namens Variant. Das sieht dann so aus:

Code:

#include <iostream>
#include <list>
#include <string>

/** Eine Variant Klasse */
class Variant {
private:

	// Elementbasisdatentyp
	class elem {
	public:
		virtual ~elem() { }
		// Abstrakte Methoden
		virtual const std::type_info& type() const = 0;
		virtual elem * clone() const = 0;
	};

	// Generischer Elementdatentyp
	template<typename VT> class elemType : public elem {
	public: 
		VT data;
		elemType(const VT& val) : data(val) { }
		// Typ der Klasse zurueckgeben
		virtual const std::type_info & type() const {
			return typeid(VT);
		}
		virtual elemType * clone() const {
			return new elemType(data);
		}
	};

private: // Datenelement verdecken
	elem * data; // Datenelement
	

public:
	// C'tors & D'tor
	Variant() : data(0) {};
	template<typename VT> Variant(const VT& val) : data(new elemType<VT>(val)) { }
	Variant(const Variant& other) : data(other.data ? other.data->clone() : 0) { }
	virtual ~Variant() { delete data; }
	
	Variant& swap(Variant& rhs) {
		std::swap(data, rhs.data);
		return *this;
	}
	// Operatoren
	template<typename VT> Variant& operator=(const VT& rhs) {
		Variant(rhs).swap(*this);
		return *this;
	}
	Variant& operator=(const Variant& rhs) {
		Variant(rhs).swap(*this);
		return *this;
	}
	// Methoden
	bool empty() const { return !data; }
	const std::type_info& type() const { return data ? data->type() : typeid(void); }
	// Cast-Methoden
	template<typename VT> friend VT* variant_cast(Variant* operand) {
		return operand && operand->type() == typeid(VT)
		 ? &static_cast<Variant::elemType<VT> *>(operand->data)->data
		 : 0;
	}
	template<typename VT> friend VT* variant_cast(const Variant* operand) {
		return variant_cast<VT>(const_cast<Variant *>(operand));
	}
 	template<typename VT> friend VT variant_cast(const Variant& operand) {
		VT* result = variant_cast<VT>(&operand);
		if (!result) throw(bad_variant_cast());
		return *result;
	}
 	template<typename VT> friend VT variant_cast(Variant& operand) {
		VT* result = variant_cast<VT>(&operand);
		if (!result) throw(bad_variant_cast());
		return *result;
	}

public:
	// Eine Exception, falls beim Casten etwas schieflaeuft
	class bad_variant_cast : public std::bad_cast {
	public:
		virtual const char* what() const throw() {
			return "convertion failed";
    }
  };
};

// Testklasse: Complex
class Complex {
	private: int r, i;
	public: Complex(int re, int im) : r(re), i(im) { }
	friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
		os << c.r;
		if (c.i>0) os <<'+';
		os << c.i << 'i';
		return os;
	}
};

bool isInt(const Variant& v) { return v.type() == typeid(int); }
bool isDouble(const Variant& v) { return v.type() == typeid(double); }
bool isBool(const Variant& v) { return v.type() == typeid(bool); }
bool isCharPtr(const Variant& v) {
	try {
		variant_cast<const char *>(v);
		return true;
	} catch(const Variant::bad_variant_cast& {
		return false;
	}
}
bool isString(const Variant& v) { return variant_cast<std::string>(&v); }
bool isComplex(const Variant& v) { return variant_cast<Complex>(&v); }


int main() {
	std::list<Variant> l;
	l.push_back(Variant(4));
	l.push_back(Variant(true));
	l.push_back(Variant((const char*)"Hello World"));
	l.push_back(Variant((std::string)"Hello World"));
	l.push_back(Variant(10));
	l.push_back(Variant(17.5));
	l.push_back(Variant(Complex(1,2)));
	// Anzahl der Integer-Elemente ausgeben
	std::cout << std::count_if(l.begin(), l.end(), isInt) << std::endl;
	// Alle double, char*, std::string und complex Elemente ausgeben
	for (std::list<Variant>::iterator i=l.begin(); i!=l.end(); i++) {
		if (isDouble(*i)) std::cout << variant_cast<double>(*i) << std::endl;
		if (isCharPtr(*i)) std::cout << variant_cast<const char*>(*i) << std::endl;
		if (isString(*i)) std::cout << variant_cast<std::string>(*i) << std::endl;
		if (isComplex(*i)) std::cout << variant_cast<Complex>(*i) << std::endl;
	}
	return 0;
}



Zitat:
Edit: Auszusetzen ist noch die Angabe des Datentyps in der getData-Funktion, da dieser ja schon durch den ersten Parameter bestimmt ist.


Das wird dir auch nicht erspart bleiben (auszer du machst das ganze auf eine andere Art), schon alleine wegen der Standardkonvertierung nicht.
 Beiträge: 3.842 | Punkte: 4.429 | Wohnort: Wien | Registriert seit: 4060 Tagen (Aug 2003)
 
Neuer Beitrag 28.01.2008, 13:46
 Re: Variierender Datentyp

#6 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich

Themenstarter
Wahahahaha ich habs doch geschafft Ich hab aber ewig dafür gebraucht. Folgende Änderungen an meinem letzten Posting machen es möglich:

Code:

struct Data {
...
            // Castoperator definieren.
            template <class T>
            operator T () const {
                const BelData<T>* me = dynamic_cast<const BelData<T>*>(this);
                assert( me != NULL );
                return me->operator T();
            }
};

...

template <class T>
struct BelData : public Data
{
...

           // Typumwandlung.
           operator T () const { return inhalt; }
};

/* Und hinter den beiden Klassen (wieder öffentlich) wird am Ende des Datentupels die neue getData-Methode ergänzt (die alte wird entfernt). */

// Am Ende von class Datentupel { 
        // Gibt Daten zurück.
        const Data& getData( int nr ) const { return *daten[nr]; }



Das ermöglicht jetzt die ultimative Anwendung:

Code:

int main()
{
    Datentupel infos(3);
    infos.setData(2007,0);
    infos.setData(12.3F,1);
    infos.setData(true,2);
    bool wahr = infos.getData(2);
    cout << wahr << endl;
    // Gibt 1 aus.

    return 0;
}



Mit SIcherheit lässt sich das auf die Variant-Klasse übertragen, aber dazu werde ich sie jetzt erstmal genau durchlesen. Wenn wir "Variant" ausgeschliffen haben gehts ab mit ihr in den Showcase


Edit: Wie es aussieht, müssten nur die variant_casts durch die impliziten Cast-Operatoren ersetzt werden.

Edit 2: Merkwürdig... wenn infos.getData(i) einen string enthält, funktioniert "(string) infos.getData(i)" nicht, wohl aber "infos.getData(i).operator string()". Hier muss ich wohl mit den friends herumspielen.

Beitrag editiert von koril-k am 28.01.2008, 14:09

Beitrag editiert von koril-k am 28.01.2008, 14:45
 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 29.01.2008, 17:20
 Re: Variierender Datentyp

#7 | Zitieren |

bg ist Offline bg

Operator
Zen Master Foo


Alter: 33
Geschlecht: Männlich
Hm, ich bin jedenfalls schon gespannt.. Wenn ich in der Annahme richtig liege, was du zu machen gedenkst, wuerde ich sagen, dass dies gemaesz dem C++ Standard nicht moeglich sein darf. ;-)
 Beiträge: 3.842 | Punkte: 4.429 | Wohnort: Wien | Registriert seit: 4060 Tagen (Aug 2003)
 
Neuer Beitrag 29.01.2008, 18:03
 Re: Variierender Datentyp

#8 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich

Themenstarter
Naja für die Standardtypen funktioniert es ja bereits. Ich gebe aber noch nicht auf ^^
 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 29.01.2008, 18:36
 Re: Variierender Datentyp

#9 | Zitieren |

bg ist Offline bg

Operator
Zen Master Foo


Alter: 33
Geschlecht: Männlich
So mal aus reiner Neugierde: Welchen Compiler + Version verwendest du denn?
 Beiträge: 3.842 | Punkte: 4.429 | Wohnort: Wien | Registriert seit: 4060 Tagen (Aug 2003)
 
Neuer Beitrag 30.01.2008, 13:54
 Re: Variierender Datentyp

#10 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich

Themenstarter
Ich benutze Code::Blocks Version 1.0rc2. Meinst du, ich bin mit meinen Versuchen bereits in undefinierten Gewässern?
 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 30.01.2008, 13:54
 Werbung

AdBot
Mister Ad

 
Neuer Beitrag 30.01.2008, 20:46
 Re: Variierender Datentyp

#11 | Zitieren |

bg ist Offline bg

Operator
Zen Master Foo


Alter: 33
Geschlecht: Männlich
Code::Blocks ist ja nur eine IDE, der zugehoerige Compiler ist MingW, welchen ich nicht sonderlich gut kenne, darum kann ich auch nicht recht viel sagen. Ist auf jeden Fall ein Abkoemmling des Gnu C++ Compilers, der ja bei den Standards imho nicht so schlecht abschneidet.
 Beiträge: 3.842 | Punkte: 4.429 | Wohnort: Wien | Registriert seit: 4060 Tagen (Aug 2003)
 
Neuer Beitrag 01.02.2008, 12:21
 Re: Variierender Datentyp

#12 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich

Themenstarter
Der Fehler war nur ein Schusseligkeitsfehler. Bei (string) variable wird ein String-Konstruktor gesucht, der die Variable annimmt (glaube ich). Lässt man dieses "(string)" weg, funktioniert es.

VORSICHT: Die folgendendermaßen geänderte Variant-Klasse sollte auf keinen Fall ernsthafte Anwendung finden, da der Abruf der Daten durch den von mir definierten Cast-Operator zwar funktioniert, in seiner Anwendung aber derart fehlerintolerant ist, dass man lieber die variant_casts von bg benutzen sollte! Die folgende Klasse ist nur ein künstlerisches Ideal.

Code:

#include <iostream>
#include <list>
#include <string>
#include <cassert>

/** Eine Variant Klasse */
class Variant {
private:

	// Elementbasisdatentyp
	class elem {
	public:
		virtual ~elem() { }
		// Abstrakte Methoden
		virtual const std::type_info& type() const = 0;
		virtual elem * clone() const = 0;

		template <typename T> operator T() const {
		    const elemType<T>* me = dynamic_cast< const elemType<T>* >(this);
		    assert( me != NULL );
		    return me->data;
		}
	};

	// Generischer Elementdatentyp
	template<typename VT> class elemType : public elem {
	public:
		VT data;
		elemType(const VT& val) : data(val) { }
		// Typ der Klasse zurueckgeben
		virtual const std::type_info & type() const {
			return typeid(VT);
		}
		virtual elemType * clone() const {
			return new elemType(data);
		}
	};

private: // Datenelement verdecken
	elem * data; // Datenelement


public:
	// C'tors & D'tor
	Variant() : data(0) {};
	template<typename VT> Variant(const VT& val) : data(new elemType<VT>(val)) { }
	Variant(const Variant& other) : data(other.data ? other.data->clone() : 0) { }
	virtual ~Variant() { delete data; }

	Variant& swap(Variant& rhs) {
		std::swap(data, rhs.data);
		return *this;
	}
	// Operatoren
	template<typename VT> Variant& operator=(const VT& rhs) {
		Variant(rhs).swap(*this);
		return *this;
	}
	Variant& operator=(const Variant& rhs) {
		Variant(rhs).swap(*this);
		return *this;
	}
	// Methoden
	bool empty() const { return !data; }
	const std::type_info& type() const { return data ? data->type() : typeid(void); }
	// Cast-Methoden
	template<typename VT> friend VT* variant_cast(Variant* operand) {
		return operand && operand->type() == typeid(VT)
		 ? &static_cast<Variant::elemType<VT> *>(operand->data)->data
		 : 0;
	}
	template<typename VT> friend VT* variant_cast(const Variant* operand) {
		return variant_cast<VT>(const_cast<Variant *>(operand));
	}
 	template<typename VT> friend VT variant_cast(const Variant& operand) {
		VT* result = variant_cast<VT>(&operand);
		if (!result) throw(bad_variant_cast());
		return *result;
	}
 	template<typename VT> friend VT variant_cast(Variant& operand) {
		VT* result = variant_cast<VT>(&operand);
		if (!result) throw(bad_variant_cast());
		return *result;
	}

	template <typename T> operator T() const {
	    return data->operator T();
	}

public:
	// Eine Exception, falls beim Casten etwas schieflaeuft
	class bad_variant_cast : public std::bad_cast {
	public:
		virtual const char* what() const throw() {
			return "convertion failed";
    }
  };
};



Die Anwendung schlechthin ohne Angabe des erwarteten Typs. Hinweis zum obigen Hinweis: Die Daten dürfen nicht direkt an den <<-Operator übergeben werden sondern müssen sauber über einen Zwischenwert geleitet werden.

Code:

int main() {

    Variant a[5];

    a[0] = 10;
    a[1] = 13.2;
    a[2] = std::string("Hallo");
    a[3] = true;

    std::list<int> liste;
    liste.push_back(123);
    a[4] = liste;

    int wert1 = a[0];
    double wert2 = a[1];
    std::string wert3 = a[2];
    bool wert4 = a[3];
    std::list<int> liste2 = a[4];
    int wert5 = *liste2.begin();

    std::cout << wert1 << std::endl;
    std::cout << wert2 << std::endl;
    std::cout << wert3 << std::endl;
    std::cout << wert4 << std::endl;
    std::cout << wert5 << std::endl;

    return 0;
}



So bg, wir haben die c++-Ehre vor Java verteidigt
 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 02.02.2008, 15:30
 Re: Variierender Datentyp

#13 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich

Themenstarter
Ich zerbrech mir gerade den Kopf, wie man einen Variant in einer Datei speichern und laden kann. Hast du eine Idee?
 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 03.02.2008, 00:03
 Re: Variierender Datentyp

#14 | Zitieren |

bg ist Offline bg

Operator
Zen Master Foo


Alter: 33
Geschlecht: Männlich
Du moechtest doch hoffentlich kein komplettes Framework mit Garbage Collector usw. schreiben, oder? ;-)
 Beiträge: 3.842 | Punkte: 4.429 | Wohnort: Wien | Registriert seit: 4060 Tagen (Aug 2003)
 
Neuer Beitrag 03.02.2008, 08:55
 Re: Variierender Datentyp

#15 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich

Themenstarter
Ich habe eher das Gefühl, dass wir mit unserem Variant c++ zu BASIC zurückentwickeln *g*

Nee mal im Ernst: Die Klasse hat in meinem Programm zentrale Anwendung. Es geht um Denkeinheiten (künstliche Intelligenzen), die insgesamt 300 verschiedenartige Informationstypen verarbeiten und untereinander austauschen können. Da mich weder Gott noch Teufel dazu kriegen, 300 ähnliche Klassen zu schreiben, ist der Variant sehr nützlich für mich. Ich glaube, dass mir mit hängen und würgen das Speichern von außerhalb der Variantklasse gelingen würde... da ich aber keiner äußeren Macht bei meinem Programm unterstehe (wie z.B. Zeitdruck), kann ich an jeder Stelle solange herumbasteln, bis sie einfach "schön" ist und dazu hat sich ein Variant gefälligst selbst speichern zu können.
 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 03.02.2008, 21:31
 Re: Variierender Datentyp

#16 | Zitieren |

bg ist Offline bg

Operator
Zen Master Foo


Alter: 33
Geschlecht: Männlich
Naja, mir schwebt da schon etwas vor.. ist aber "etwas" umfangreicher ;-) Vielleicht freut's mich mal ja in den naechsten Tagen... ;-)

Ein paar Gedanken gehen mir schon noch durch den Kopf... Kann man Funktionspointer serialisieren? Welcher Algorithmus am besten geeignet um Zyklen zu erkennen, usw. :-)
 Beiträge: 3.842 | Punkte: 4.429 | Wohnort: Wien | Registriert seit: 4060 Tagen (Aug 2003)
 
Neuer Beitrag 03.02.2008, 21:31
 Werbung

AdBot
Mister Ad

 
Neuer Beitrag 03.02.2008, 21:43
 Re: Variierender Datentyp

#17 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich

Themenstarter
Was wäre es für eine Erleichterung für die Variantklasse, wenn wir aus einem "elem" den Templatetyp VT rausbekämen, sodass wir innerhalb eines "elem" auf den entsprechenden "elemType<VT>" casten könnten. Frei nach dem Motto "Caste dich! Und sag mir dann deinen Typ."

Die Vergleichsoperatoren sind übrigens auch leicht zu haben.

Edit: Schade, mit den Variants sind gar keine Metaebenen möglich. Das könnte hin und wieder arge Probleme machen! Also ich habe den bel. Typ T und möchte ihn im Variant darstellen. Wenn nun T == Variant, könnte es zu unerwünschtem Verhalten kommen, da Die Variantinstanz nun den Inhalt des anderen Variants enthält und nicht den Variant selber!

Beitrag editiert von koril-k am 03.02.2008, 21:58
 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 03.02.2008, 21:59
 Re: Variierender Datentyp

#18 | Zitieren |

bg ist Offline bg

Operator
Zen Master Foo


Alter: 33
Geschlecht: Männlich
Also mittels RTTI ist es ein leichtes, den Datentyp zu ermitteln:

Folgende Memberfunktion von Variant gibt den Datentyp von elem aus.
Code:

// in Klasse Variant:
 void printType() {
  std::cout << this->data->type().name();
 }



Edit: Ja, die Variant-Klasse laesst sich nicht schachteln, also:
Code:

Variant x = Variant(Variant(5)); // erzeugt technisch dasselbe wie Variant(5)


Du kannst einen neuen Konstruktor schreiben, z.B.:
Code:

 Variant(const Variant& sub, bool bIgnore) : data(new elemType<Variant>(sub)) { }


den du dann entsprechend aufrust.

Beitrag editiert von bg am 03.02.2008, 22:05
 Beiträge: 3.842 | Punkte: 4.429 | Wohnort: Wien | Registriert seit: 4060 Tagen (Aug 2003)
 
Neuer Beitrag 03.02.2008, 22:10
 Re: Variierender Datentyp

#19 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich

Themenstarter
Ok ich hab mich nicht gut genug ausgedrückt: Ich möchte den Typ dann auch benutzen. Also wenn x eine Typinformation enthält, dass ich dann eine Instanz davon anlegen kann, wie z.B. "x a;" oder so.
 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 03.02.2008, 22:44
 Re: Variierender Datentyp

#20 | Zitieren |

bg ist Offline bg

Operator
Zen Master Foo


Alter: 33
Geschlecht: Männlich
Kurz gesagt; Das laesst sich nicht im Allgemeinen realisieren. Wir haben ja bis jetzt immer Objekte bekannten Typs erzeugt und mit Template-Tricks gearbeitet. Damit kann der Compiler den Datentyp im Endeffekt zum Compile-Zeitpunkt feststellen (wenns auch etwas Mehrarbeit fuer ihn ist ;-).
Objekte eines unbekannten Datentyps lassen sich meines Wissensstandes nach nicht erzeugen.
 Beiträge: 3.842 | Punkte: 4.429 | Wohnort: Wien | Registriert seit: 4060 Tagen (Aug 2003)
 
Neuer Beitrag 04.02.2008, 09:36
 Re: Variierender Datentyp

#21 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich

Themenstarter
Sowas hat Darii am Anfang auch gesagt Der Compiler dürfte den Datentyp nicht zwangsläufig kennen:

Code:

Variant v;
if( eingabe == 0 ) v = 10;
else v = true;
// Welchen Datentyp beinhaltet v?

 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 04.02.2008, 13:54
 Re: Variierender Datentyp

#22 | Zitieren |

bg ist Offline bg

Operator
Zen Master Foo


Alter: 33
Geschlecht: Männlich
Es findet ja auch noch eine Ueberpruefung zur Laufzeit statt.

Einen Variant vom Typ X zu erzeugen ist kein Problem. Wenn ich die Daten aus Variant wieder haben will, muss ich ja einen Datentypen angeben. Falls eine Konvertierung zur Laufzeit fehlschlaegt, gibts einen Fehler.
 Beiträge: 3.842 | Punkte: 4.429 | Wohnort: Wien | Registriert seit: 4060 Tagen (Aug 2003)
 
Neuer Beitrag 06.02.2008, 20:00
 Re: Variierender Datentyp

#23 | Zitieren |

Darii ist Offline Darii
Ancient Hacker



Alter: 29
Geschlecht: Männlich
Warum nimmst du nicht einfach eine andere Sprache? Ich verstehe von dieser Klasse nichtmal die Hälfte aber es sieht für mich doch irgendwie sehr gekrampft aus.
 Beiträge: 1.042 | Punkte: 1.155 | Wohnort: Hannover(Region) | Registriert seit: 4517 Tagen (May 2002)
 
Neuer Beitrag 07.02.2008, 09:23
 Re: Variierender Datentyp

#24 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich

Themenstarter
Ich nehme keine andere Sprache, weil ich außer c++ nur niedere Sprachen wie Basic, Prolog usw. kann. Außerdem ist c++ so eine malerische, wunderschöne Sprache (wie man ja an unserer Klasse sieht), dass ich mich einfach nicht von ihr trennen kann.
 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 17.02.2008, 16:59
 Re: Variierender Datentyp

#25 | Zitieren |

bg ist Offline bg

Operator
Zen Master Foo


Alter: 33
Geschlecht: Männlich
Wahrscheinlich werden dir dann die Neuerung, die im C++0x Standard (hoffentlich) 2009 kommen, sehr gut gefallen.

U.a. sollte dann folgendes moeglich sein:
Code:

#include <iostream>
#include <vector>
int main() {
    std::vector<int> v;
    v.push_back(4);
    v.push_back(2);
    for ( decltype(v.begin()) i = v.begin(); i != v.end(); ++i ) {
        std::cout << (*i);
    }
}



Oder noch besser, folgender Code:
Code:

int main() {
    auto a = 4;
    std::vector<int> v;
    v.push_back(a);
    v.push_back(2);
    for ( auto i = v.begin(); i != v.end(); ++i ) {
        // ...
    }
}



Hm, vielleicht schafft es ja das auto-Konzept in gcc 4.4? ;-)

PS: Ich hab mal ein wenig an einem Weg zur Serialisierung gearbeitet. Werde noch ein wenig daran feilen und dann vorstellen :-)
 Beiträge: 3.842 | Punkte: 4.429 | Wohnort: Wien | Registriert seit: 4060 Tagen (Aug 2003)
 
Neuer Beitrag 17.02.2008, 17:22
 Re: Variierender Datentyp

#26 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich

Themenstarter
Das würde mir tatsächlich gefallen!

Zitat:
PS: Ich hab mal ein wenig an einem Weg zur Serialisierung gearbeitet. Werde noch ein wenig daran feilen und dann vorstellen :-)


Nach dem Schema "meinVariant->lade<typ>(datei)" ist es kein Problem. Oder hast du es etwa ohne die Typangabe geschafft??? In diesem Fall:
 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 25.02.2008, 07:39
 Re: Variierender Datentyp

#27 | Zitieren |

bg ist Offline bg

Operator
Zen Master Foo


Alter: 33
Geschlecht: Männlich
Ok. Ich hab zur Zeit leider nicht so viel Freizeit wie ich mir gerne wuenschen wuerde, deshalb bin ich in letzter Zeit auch noch nicht recht gekommen.

Ich stell hier mal kurz vor, wie ich das ca. vorstelle:

Ich hab einige Teile der Variante Klasse weggelassen, die wir hier nicht brauchen. Ich hab eine Struktur opType eingefuehrt, die uns Funktionszeiger auf die entsprechenden Klassenmethoden speichern. Interessant sind bei der Serialisierung natuerlich die Stream-Operatoren.

<< hab ich hier relativ einfach geloest, beim >>-Operator wird man diesen stark Ueberladen muessen.

Ich hab hier ein paar Teile von Variant weggelassen, damit das Ganze nicht zu unuebersichtlich wird:
Code:

#include <iostream>
#include <string>
#include <cassert>


// Diese Klasse speichert uns die Funktionszeiger auf die gewuenschten Operatoren eines Typs
struct opType {
	// Wrapper-Funktion fuer operator<<
	template <typename VT>
	static std::ostream& ostream_operator(std::ostream& rStream, const void* pValue) {
		return rStream << *reinterpret_cast<VT*>(pValue);
	}
	// TODO: zusaetzliche Operatoren definieren

	// Funktionszeiger
	std::ostream& (*p_ostream_operator)(std::ostream&, const void*);

	template <typename VT>
	static opType& get(VT& v) {
		static opType operators;
		static bool initialized = false;
 
		// jeden Typ nur 1x initialisieren
		if (!initialized) {
			operators.p_ostream_operator = &ostream_operator<VT>;
			// TODO: weitere Operatorenzeiger
			initialized = true;
		}
		return operators;
	}
};

class Variant {
private:

	// Elementbasisdatentyp
	class elem {
	public:
		virtual ~elem() { }
		// Abstrakte Methoden
		virtual const std::type_info& type() const = 0;
		virtual elem * clone() const = 0;
		virtual std::ostream& ostream_operator(std::ostream& stream) const = 0;

		template <typename T> operator T() const {
		    const elemType<T>* me = dynamic_cast< const elemType<T>* >(this);
		    //if (!me) throw bad_cast_exception();
		    return me->data;
		}
	};

	// Generischer Elementdatentyp
	template<typename VT> class elemType : public elem {
	public:
		VT data;
		opType& operators;

		elemType(const VT& val) : data(val), operators(opType::get(val)) { }
		// Typ der Klasse zurueckgeben
		virtual const std::type_info & type() const {
			return typeid(VT);
		}
		virtual elemType * clone() const {
			return new elemType(data);
		}
		virtual std::ostream& ostream_operator(std::ostream& rStream) const {
			return (*operators.p_ostream_operator)(rStream, &data);
		}
	};

private: // Datenelement verdecken
	elem * data; // Datenelement


public:
	// C'tors & D'tor
	Variant() : data(0) {};
	template<typename VT> Variant(const VT& val) : data(new elemType<VT>(val)) { }
	Variant(const Variant& other) : data(other.data ? other.data->clone() : 0) { }
	virtual ~Variant() { delete data; }

	Variant& swap(Variant& rhs) {
		std::swap(data, rhs.data);
		return *this;
	}
	// Operatoren
	template<typename VT> Variant& operator=(const VT& rhs) {
		Variant(rhs).swap(*this);
		return *this;
	}
	Variant& operator=(const Variant& rhs) {
		Variant(rhs).swap(*this);
		return *this;
	}
	// Methoden
	bool empty() const { return !data; }

	template <typename T> operator T() const {
	    return data->operator T();
	}

	friend std::ostream& operator<<(std::ostream& rStream, const Variant& rhs) {
	// TODO: Typueberpruefung sollte hier erfolgen
	return rhs.data->ostream_operator(rStream);
}

};

int main() {
	Variant a[4];

	a[0] = 10UL;
	a[1] = 13.2;
	a[2] = std::string("Hallo");
 	a[3] = true;

	for (int i=0; i<4; i++) { std::cout << a[i] << std::endl; }
	return EXIT_SUCCESS;
}



Mehr Details zum >>-Operator demnaechst :-)
 Beiträge: 3.842 | Punkte: 4.429 | Wohnort: Wien | Registriert seit: 4060 Tagen (Aug 2003)
 
Neuer Beitrag 25.02.2008, 10:36
 Re: Variierender Datentyp

#28 | Zitieren |

koril-k ist Offline koril-k
Ancient Hacker



Alter: 27
Geschlecht: Männlich

Themenstarter
Diese Methode, neue Datentypen in das Variant-System bei ihrem ersten Auftreten zu integrieren, erinnert mich stark an meinen gedanklichen Entwurf, eine Art Virtual Method Table für die Variant-Klasse zu schreiben. Da ich das aber als Array plante, hab ich die Idee schnell verworfen. Deine Umsetzung ist mal wieder ein kleines Meisterwerk der Polymorphie, das meinen Programmierer-Horizont erneut erweitert hat. Schade, dass ich die nächsten Wochen nicht an meinem eigenen PC bin, dann würde ich sofort an deiner Idee herumbasteln. Schade ist nur, dass der eben beschriebene Ansatz wohl kaum helfen würde, einen >>-Operator zu schreiben, da der zu lesende Datentyp des Datenstrominhalts in einer Anweisung wie "datenstrom >> variant" nicht bekannt sein dürfte.

Meine Gedanken kreisen gerade von der direkten Manipulation der Bits eines Varians bis hin zur üblen Verwendung von Makros. Aber all das ist keine angebrachte Lösung.
 Beiträge: 1.133 | Punkte: 1.996 | Wohnort: Greifswald | Registriert seit: 3122 Tagen (Mar 2006)
 
Neuer Beitrag 06.03.2008, 23:40
 Re: Variierender Datentyp

#29 | Zitieren |

bg ist Offline bg

Operator
Zen Master Foo


Alter: 33
Geschlecht: Männlich
Eine noch nicht ganz perfekte Variant-Klasse mit Serialisierung ;-)

Was noch nicht funktioniert:
- Serialisierung von STL-Containern
- Streams sollten in einem sinnvollen Format geschrieben sein (XML?)

Teil1
Code:

#include <string>
#include <iostream>
#include <fstream>
#include <map>
#include <memory>
#include <cassert>
#include <utility>

#define __LONG_LONG

class RegEntry;
class SerializableBase;
class Variant;

// Instanzierungsmethode
typedef SerializableBase* (*FactoryMethod)();

// Class dient zum Nachschlagen & Instanzieren registrierter Klassen
class Class {
public:
  // Klasse aus std::string ermitteln
  static Class forName(std::string s) {
    return(Class(s));
  }
  // Klasse instanzieren
  SerializableBase* newInstance() {
    assert(fmMap != NULL);
    FactoryMethodMap::iterator it = fmMap->find(name__);
    if(it != fmMap->end()) {
      return((SerializableBase*)it->second());
    } else {
      return(0);
    }
  }
private:
  std::string name__;
  // String->Funktionszeiger
  typedef std::map<std::string, FactoryMethod> FactoryMethodMap;
  static FactoryMethodMap* fmMap;
  friend class RegEntry;
  // Registrieren einer Klasse
  static void Register(std::string s, FactoryMethod m) {
    if(fmMap == NULL) {
      fmMap = new std::map<std::string, FactoryMethod>;
    }
    fmMap->insert(std::pair<std::string, FactoryMethod>(s, m));
  }
  Class(std::string s) : name__(s) {}
};

// Hilfsklasse
struct RegEntry {
  RegEntry(const char s[], FactoryMethod f) {
    Class::Register(s, f);
  }
};

/**
 * Abstrakte Basisklasse. Gibt die Schnittstellen für die Serialisierung,
 * konkret: marschall und unmarshall, vor.
 */
class SerializableBase {
public:
  /* Daten an Strom schreiben */
  virtual bool marshall(std::ostream& os) const = 0;
  /* Daten von Strom lesen */
  virtual bool unmarshall(std::istream& is) = 0;
  virtual const char* getClassName() const = 0;
  virtual Variant createVariant() const = 0;
};

/* Serialisierungs-Basisklasse.
 * Alle Klassen, die Serialiserung unterstuetzen, muessen von Serializable
 * abgeleitet sein
 */
template<typename VT, const char S[]> class Serializable :
  public SerializableBase {
public:
  static SerializableBase* newInstance() {
    VT* vt = new VT();
    return vt;
  }
  virtual ~Serializable() {}
  virtual const char* getClassName() const { return S; }
protected:
  Serializable() { const RegEntry& dummy = r; }
  virtual Variant createVariant() const;
private:
  static const RegEntry r;
};

// Makro zum Registrieren einer Klasse
/*
#define REG_CLASS( C ) \
const char C##Name__[] = #C ; \
class C : public Serializable< C, C##Name__ >
*/

// Initialisieren von statischen Datenelementen
Class::FactoryMethodMap* Class::fmMap;

template<typename VT, const char S[]> const RegEntry 
Serializable<VT, S>::r = RegEntry(S, Serializable<VT,S>::newInstance);

template<typename VT, const char S[]> Variant Serializable<VT, S>::createVariant() const {
  const VT* vt = dynamic_cast<const VT*>(this);
  Variant v = *vt;
  return v;
}

/* TValue implementiert grundlegende Methoden zum Serialisieren von beliebigen
 * Klassen. Es wird exzessiver gebrauch der Template-Ueberladung gemacht.
 */
class TValue {
private:
  std::string type__; // Name des Datentyps
  bool simple__; // Einfacher Datentyp (int, bool, float, ...)
  bool constness__; // Konstanter Datentyp
  
  // Private C'Tors
  TValue(const char* s, bool c, bool bs = true) : type__(s), constness__(c),
    simple__(bs) {}
  TValue(const char* s) : type__(s), constness__(false), simple__(true) {}
  TValue(std::string s) : type__(s), constness__(false), simple__(true) {}
  TValue() {}
  
  // Templatefunktion zum Lesen von einem Datenstrom
  template <typename VT> static VT readFromStream(std::istream& is) {
    VT v = 0; is >> v; return v;
  }
  // Templatefunktion zum Lesen einer konstanten Variable von einem Datenstrom
  template <typename VT> static const VT readFromStreamConst(std::istream& is) {
    VT v = 0; is >> v; return v;
  }
  // Liest einen const char*-String von einem Datenstrom
  static const char* readFromStreamConstChar(std::istream& is);
  // Liest ein SerializableBase-Objekt von einem Datenstrom
  static SerializableBase* readFromStreamSB(std::istream& is, std::string& className) {
    SerializableBase* sb = Class::forName(className).newInstance();
    sb->unmarshall(is);
    return sb;
  }
  
public:
  // Ermitteln des Typnamens einer Variable
  template <typename VT> static TValue lookup(VT v) {
    return TValue("void");
  }
  bool isSimple() { return simple__; }
  bool isConst() { return constness__; }
  std::string getTypename() { return type__; }
  
  friend std::ostream& operator<<(std::ostream& os, const TValue& v) {
    os << v.simple__  << " " << v.constness__ << " " << v.type__ << " ";
    return os;
  }
  friend std::istream& operator>>(std::istream& is, TValue& v) {
    is >> v.simple__ >> v.constness__ >>  v.type__;
    return is;
  }
  // Schreibt Variant-Objekt in einen Datenstrom
  static void writeToStream(std::ostream& os, Variant& v);
  // Liest ein Variant-Objekt von einem Datenstrom
  static void createFromStream(std::istream& is, Variant& var);
};

// Spezialisierung der Lookup-Funktion
template<> inline TValue TValue::lookup<const bool>(const bool) { return TValue("bool", true); }
template<> inline TValue TValue::lookup<bool>(bool) { return TValue("bool"); }
template<> inline TValue TValue::lookup<const char>(const char) { return TValue("char", true); }
template<> inline TValue TValue::lookup<char>(char) { return TValue("char"); }
template<> inline TValue TValue::lookup<const char*>(const char*) { return TValue("charptr", true); }
template<> inline TValue TValue::lookup<char*>(char*) { return TValue("charptr"); }
template<> inline TValue TValue::lookup<const short int>(const short int) { return TValue("short_int", true); }
template<> inline TValue TValue::lookup<short int>(short int) { return TValue("short_int"); }
template<> inline TValue TValue::lookup<const int>(const int) { return TValue("int", true); }
template<> inline TValue TValue::lookup<int>(int) { return TValue("int"); }
template<> inline TValue TValue::lookup<const long int>(const long int) { return TValue("long_int", true); }
template<> inline TValue TValue::lookup<long int>(long int) { return TValue("long_int"); }
#ifdef __LONG_LONG
template<> inline TValue TValue::lookup<const long long>(const long long) { return TValue("long_long", true); }
template<> inline TValue TValue::lookup<long long>(long long) { return TValue("long_long"); }
#endif
template<> inline TValue TValue::lookup<const float>(const float) { return TValue("float", true); }
template<> inline TValue TValue::lookup<float>(float) { return TValue("float"); }
template<> inline TValue TValue::lookup<const double>(const double) { return TValue("double", true); }
template<> inline TValue TValue::lookup<double>(double) { return TValue("double"); }
template<> inline TValue TValue::lookup<const long double>(const long double) { return TValue("long_double", true); }
template<> inline TValue TValue::lookup<long double>(long double) { return TValue("long_double"); }
template<> inline TValue TValue::lookup<const unsigned char>(const unsigned char) { return TValue("u_char", true); }
template<> inline TValue TValue::lookup<unsigned char>(unsigned char) { return TValue("u_char"); }
template<> inline TValue TValue::lookup<const unsigned short int>(const unsigned short int) { return TValue("u_short_int", true); }
template<> inline TValue TValue::lookup<unsigned short int>(unsigned short int) { return TValue("u_short_int"); }
template<> inline TValue TValue::lookup<const unsigned int>(const unsigned int) { return TValue("u_int", true); }
template<> inline TValue TValue::lookup<unsigned int>(unsigned int) { return TValue("u_int"); }
template<> inline TValue TValue::lookup<const unsigned long int>(const unsigned long int) { return TValue("u_long_int", true); }
template<> inline TValue TValue::lookup<unsigned long int>(unsigned long int) { return TValue("u_long_int"); }
#ifdef __LONG_LONG
template<> inline TValue TValue::lookup<const unsigned long long>(const unsigned long long) { return TValue("u_long_long", true); }
template<> inline TValue TValue::lookup<unsigned long long>(unsigned long long) { return TValue("u_long_long"); }
#endif
template<> inline TValue TValue::lookup<const SerializableBase*>(const SerializableBase* sb) { return TValue(sb->getClassName(), true, false); }
// Spezialisierung der readFromStream-Funktion
template<> char* TValue::readFromStream<char*>(std::istream&);



Beitrag editiert von bg am 06.03.2008, 23:44
 Beiträge: 3.842 | Punkte: 4.429 | Wohnort: Wien | Registriert seit: 4060 Tagen (Aug 2003)
 
Neuer Beitrag 06.03.2008, 23:41
 Re: Variierender Datentyp

#30 | Zitieren |

bg ist Offline bg

Operator
Zen Master Foo


Alter: 33
Geschlecht: Männlich
Teil2
Code:

// Die Variant Klasse kann Objekte beliebiger Datentypen aufnehmen
class Variant {
friend class TValue;
private:
  // Elementbasisdatentyp
  class elem {
  public:
    virtual ~elem() { }
    // Abstrakte Methoden
    virtual const std::type_info& type() const = 0;
    virtual elem * clone() const = 0;

    template <typename T> operator T() const {
      const elemType<T>* me = dynamic_cast< const elemType<T>* >(this);
      if (!me) throw bad_variant_cast();
      return me->data__;
    }
    // Liefert einen Zeiger auf das Datenobjekt zurueck
    virtual const void* getPtr() const = 0;
    // schreibt das Datenelement an einen Datenstrom
    virtual void writeElem(std::ostream& os) const = 0;
  };

  // Generischer Elementdatentyp
  template<typename VT> class elemType : public elem {
  public:
    VT data__;

    elemType(const VT& val) : data__(val) { }
    // Typ der Klasse zurueckgeben
    virtual const std::type_info & type() const {
      return typeid(VT);
    }
    // Datenelement kopieren (Copy C'tor)
    virtual elemType * clone() const {
      return new elemType(data__);
    }
    const void* getPtr() const { return &data__; }
    virtual void writeElem(std::ostream& os) const {
      os << TValue::lookup<VT>(data__) << data__;
    }
  };
  
private:
  elem * data__; // Datenelement verdecken
  bool serializableBase__; /* data__ ist ein benutzerdefinierter Typ, der von
                            * SerializableBase abgeleitet ist, und somit seine
                            * eigene Schnittstelle mitbringt */
  
public:
  // erzeugt ein Variant Objekt, das seine eigene Schnittstelle hat
  template <typename VT> static Variant createSerializable(const VT& var) {
    Variant v = var;
    v.serializableBase__ = true;
    return v;
  }
  
  // C'tors & D'tor
  Variant() : data__(0), serializableBase__(false) {};
  template<typename VT> Variant(const VT& val) : data__(new elemType<VT>(val)),
    serializableBase__(false) {}

  Variant(const Variant& other) : data__(other.data__ ? other.data__->clone() : 0),
   serializableBase__(other.serializableBase__) { }
  virtual ~Variant() { delete data__; }
  
  // Swap-Methode (spart moeglicherweise beim Kopieren und Zuweisen Aerger)
  Variant& swap(Variant& rhs) {
    std::swap(data__, rhs.data__);
    std::swap(serializableBase__, rhs.serializableBase__);
    return *this;
  }
  
  bool empty() const { return !data__; }
  bool isSerializableBase() { return serializableBase__; }
  const SerializableBase* getSerializableBasePtr() {
    const SerializableBase* vPtr =
      reinterpret_cast<const SerializableBase*>(data__->getPtr());
    return vPtr;
  }
  
  template<typename VT> Variant& operator=(const VT& rhs) {
    Variant(rhs).swap(*this);
    return *this;
  }
  Variant& operator=(const Variant& rhs) {
    Variant(rhs).swap(*this);
    return *this;
  }
  
  const std::type_info& type() const { return data__ ? data__->type() : typeid(void); }

  template <typename T> operator T() const {
    return data__->operator T();
  }

 // Eine Exception, falls beim Casten etwas schieflaeuft
  class bad_variant_cast : public std::bad_cast {
  public:
    virtual const char* what() const throw() {
      return "convertion failed";
    }
  };
};

// Spezialisierung von Variant::elemType::writeElem
template<> void Variant::elemType<char*>::writeElem(std::ostream& os) const {
  char* s = data__;
  int len = strlen(s);
  TValue tv = TValue::lookup<char*>(data__);
  os << tv << len << ' ';
  os.write(s, len);
}
// Spezialisierung von Variant::elemType::writeElem
template<> void Variant::elemType<const char*>::writeElem(std::ostream& os) const {
  const char* s = data__;
  int len = strlen(s);
  TValue tv = TValue::lookup<const char*>(data__);
  os << tv << len << ' ';
  os.write(s, len);
}

// Implementierung von TValue-Funktionen
void TValue::writeToStream(std::ostream& os, Variant& v){
  if (v.isSerializableBase()) {
    const SerializableBase* sb = v.getSerializableBasePtr();
    TValue tv = TValue::lookup<const SerializableBase*>(sb);
    os << tv;
    sb->marshall(os);
  } else {
    v.data__->writeElem(os);
  }
  os << " ";
}

void TValue::createFromStream(std::istream& is, Variant& var) {
  TValue t;
  is >> t;
  
  if (!t.simple__) {
    var = readFromStreamSB(is, t.type__)->createVariant();
    var.serializableBase__ = true;
  } else {
    if (t.constness__) {
      if (t.type__ == "int") var = readFromStreamConst<int>(is);
      else if(t.type__ == "charptr") var = readFromStreamConstChar(is);
      else if(t.type__ == "double") var = readFromStreamConst<double>(is);
      else if(t.type__ == "char") var = readFromStreamConst<char>(is);
      else if(t.type__ == "float") var = readFromStreamConst<float>(is);
      else if(t.type__ == "bool") var = readFromStreamConst<bool>(is);
      else if(t.type__ == "long_int") var = readFromStreamConst<long int>(is);
      #ifdef __LONG_LONG
      else if(t.type__ == "long_long") var = readFromStreamConst<long long>(is);
      #endif
      else if(t.type__ == "short_int") var = readFromStreamConst<short int>(is);
      else if(t.type__ == "long_double") var = readFromStreamConst<long double>(is);
      else if(t.type__ == "u_int") var = readFromStreamConst<unsigned int>(is);
      else if(t.type__ == "u_char") var = readFromStreamConst<unsigned char>(is);
      else if(t.type__ == "u_long_int") var = readFromStreamConst<unsigned long int>(is);
      #ifdef __LONG_LONG
      else if(t.type__ == "u_long_long") var = readFromStreamConst<unsigned long long>(is);
      #endif
      else if(t.type__ == "u_short_int") var = readFromStreamConst<unsigned short int>(is);
      else if(t.type__ == "void") { std::cout << "Something bad happened (exception here)" << std::endl; }
      else { std::cout << "Something even more bad happened here (exception)" << std::endl; }
    } else {
      if (t.type__ == "int") var = readFromStream<int>(is);
      else if(t.type__ == "charptr") var = readFromStream<char*>(is);
      else if(t.type__ == "double") var = readFromStream<double>(is);
      else if(t.type__ == "char") var = readFromStream<char>(is);
      else if(t.type__ == "float") var = readFromStream<float>(is);
      else if(t.type__ == "bool") var = readFromStream<bool>(is);
      else if(t.type__ == "long_int") var = readFromStream<long int>(is);
      #ifdef __LONG_LONG
      else if(t.type__ == "long_long") var = readFromStream<long long>(is);
      #endif
      else if(t.type__ == "short_int") var = readFromStream<short int>(is);
      else if(t.type__ == "long_double") var = readFromStream<long double>(is);
      else if(t.type__ == "u_int") var = readFromStream<unsigned int>(is);
      else if(t.type__ == "u_char") var = readFromStream<unsigned char>(is);
      else if(t.type__ == "u_long_int") var = readFromStream<unsigned long int>(is);
      #ifdef __LONG_LONG
      else if(t.type__ == "u_long_long") var = readFromStream<unsigned long long>(is);
      #endif
      else if(t.type__ == "u_short_int") var = readFromStream<unsigned short int>(is);
      else if(t.type__ == "void") { std::cout << "Something bad happened (exception here)" << std::endl; }
      else { std::cout << "Something even more bad happened here (exception)" << std::endl; }
    }
  }
}

template<> char* TValue::readFromStream<char*>(std::istream& is) {
  int len;
  is >> len;
  char* c = new char[len+1];
  is.read(c, 1);
  is.read(c, len);
  c[len] = '\0';
  return c;
}

const char* TValue::readFromStreamConstChar(std::istream& is) {
  int len;
  is >> len;
  char* c = new char[len+1];
  is.read(c, 1);
  is.read(c, len);
  c[len] = '\0';
  return c;
}

extern const char CCord__[] = "Cord";
// Testklasse Cord
// Oder Makro: REG_CLASS(Cord) {
class Cord : public Serializable<Cord, CCord__> {
private:
  int x__,y__;
public:
  Cord() {}
  Cord(int x, int y) : x__(x), y__(y) {}
  friend std::ostream& operator<<(std::ostream& os, const Cord& c) {
    os << "{x,y = " << c.x__ << " " << c.y__ << "}" << std::endl;
    return os;
  }
  virtual bool marshall(std::ostream& os) const {
    os << x__ << " " << y__;
    return true;
  }
  virtual bool unmarshall(std::istream& is) {
    is >> x__ >> y__;
    return true;
  }
  
};

int main(int argc, char** argv) {
  Variant a1 = (char*)"Hello World";
  Variant a2 = 42;
  Variant a3 = (bool)true;
  #ifdef __LONG_LONG
  Variant a4 = (unsigned long long) 0xFFFFFFFFFFFFFFF0;
  #else
  Variant a4 = (unsigned long) 0xFFFFFFF0;
  #endif
  Variant a5 = (float) 25.4;
  Variant a6 = (short int) -32500;
  Variant a7 = 2.3;
  Variant a8 = Variant::createSerializable(Cord(4,-5));
  char* b1;
  int b2;
  bool b3;
  #ifdef __LONG_LONG
  unsigned long long b4;
  #else
  unsigned long b4;
  #endif
  float b5;
  short int b6;
  double b7;
  Cord b8;
  Variant c[8];
  
  // #ifdef __SUN_PRO_CC
  std::ofstream out("foo.txt");
  TValue::writeToStream(out, a1);
  TValue::writeToStream(out, a2);
  TValue::writeToStream(out, a3);
  TValue::writeToStream(out, a4);
  TValue::writeToStream(out, a5);
  TValue::writeToStream(out, a6);
  TValue::writeToStream(out, a7);
  TValue::writeToStream(out, a8);
  out.close();
  
  std::ifstream in("foo.txt");
  for (int i=0; i<8; i++) TValue::createFromStream(in, c[i]);
  in.close();
  
  b1 = c[0];
  b2 = c[1];
  b3 = c[2];
  b4 = c[3];
  b5 = c[4];
  b6 = c[5];
  b7 = c[6];
  b8 = c[7];
  
  std::cout << "b1(char*): " << b1 << std::endl;
  std::cout << "b2(int): " << b2 << std::endl;
  std::cout << "b3(bool): " << b3 << std::endl;
  std::cout << "b4(unsigned long long): " << b4 << std::endl;
  std::cout << "b5(float): " << b5 << std::endl;
  std::cout << "b6(short int): " << b6 << std::endl;
  std::cout << "b7(double): " << b7 << std::endl;
  std::cout << "b8(Cord): " << b8 << std::endl;

  return (EXIT_SUCCESS);
}

 Beiträge: 3.842 | Punkte: 4.429 | Wohnort: Wien | Registriert seit: 4060 Tagen (Aug 2003)
 
Neuer Beitrag 06.03.2008, 23:41
 Werbung

AdBot
Mister Ad

Seiten (2): [1], 2 »

  

xpBulletin Board » Programmierung » C und C++ - Forum » Variierender Datentyp

[ Neue Antwort ]


Heute ist der 23.09.2014, 10:18 Uhr

Sämtliche Beiträge geben die Meinung des jeweiligen Verfassers wieder.
Für den Inhalt der Beiträge sind ausschließlich die Autoren verantwortlich.


Powered by: xpBulletin Board Version 2.3.0
Generiert in 0.2037 Sekunden
« Übersicht | @Twitter | @Facebook | Sitemap | Impressum »