C++ Suche generischen Datentyp, der erst zur Laufzeit festgelegt wird

n006

Cadet 3rd Year
Registriert
Feb. 2009
Beiträge
63
Hallo,

ich habe ein kleines Problem und weiß leider nicht wie ich es lösen soll.
Ich habe zwei Dateien A und B.

Datei A ist eine csv (comma seperated value), mit Werten verschiedener Typen an verschiedenen Positionen. Die Reihenfolge der verschiedenen Datentypen bleibt natürlich fest für alle weiteren Zeilen.

Datei B ist ebenfalls eine csv, allerdings beschreibt diese lediglich die Spalten aus Datei A. Das heißt jede Zeile aus B beschreibt eine Spalte in A

Beispiel:
Datei A:
7,blau,1.3,(1.2;1.2;0.34),Ja
3,schwarz, 5.6,(0.021;0.021;0.221),Nein
...

Datei B:
grade, integer, continuous
color, string, discrete
size, double, continuous
position, vector<double>, multivariate
class, string, discrete

Wie ist es nun zur Laufzeit möglich ein Datenkonstrukt zu erstellen, das mithilfe von Datei B, Datei A einlesen und mit den richtigen Datentypen speichern kann?

Es geht hier nicht darum die ich einen string in ein int oder double umwandeln kann, das ist mir schon klar, aber wie kann ich das realisieren.

Ich hatte schon an Templates gedacht, aber dabei werden Datentypen bereits zur Compilezeit festgelegt.

Hat jemand eine Idee?

Vielen Dank im Voraus,

euer n006
 
Mit Datentyp meinst du sowas wie string, int oder float? Kennst du alle möglichen Datentypen?
Ich würde das einfach mithilfe von regular expressions analysieren, das ist wohl das einfachste (vorausgesetzt, du kennst alle Typen).


Gruß,

badday
 
Wie wärs mit einer kleinen Containerklasse, die nur pointer entgegen nimmt und intern die pointer auf (void*) castet und den typ speichert (und im destructor evt. wieder zurückcastet und löscht)?
PHP:
enum myContainerType {
    NODATASET,
    INT,
    UINT,
    STDSTRING,
    //usw..
};
class myContainer {
    public:
        myContainerType GetType();
        
        bool SetInt(const int *number);
        bool SetUInt(const unsigned int *number);
        bool SetStdString(const std::string *string);
        //usw...
        
        bool GetInt(int *number);
        bool GetUInt(unsigned int *number);
        bool GetStdString(std::string *string);
        //usw...
    
    private:
        myContainerType type;
        void *data;
};

Und für deine Tabellenstruktur dann:
PHP:
std::vector< std::vector< myContainer  > >

/edit: Wie badday bereits bemerkt hat, musst du dazu natürlich schon alle möglichen Typen berücksichtigen!
Direkte Dynamische Typisierung gibts meistens nur in typenschwachen Skriptsprachen, d.h. nicht in C(++)!
 
Zuletzt bearbeitet:
Ein Ansatz ohne Zeiger wäre wohl mit boost::variant möglich, allerdings habe ich das noch nie benutzt.
 
c++ hat direkt sowas wie du brauchst nicht im angebot. das liegt schon alleine daran, dass c++ streng typisiert ist. d.h. du musst dir selbst was dazu basteln.
 
Ein möglicher Ansatz:

Code:
#include <iostream>
#include <cassert>
#include <string>

enum ContainerType
{
	CONTAINER_TYPE_NONE,
	CONTAINER_TYPE_INT,
	CONTAINER_TYPE_UINT,
	CONTAINER_TYPE_STRING
	// ...
};

const std::string containerTypeEnumToString( ContainerType enumVal )
{
	switch ( enumVal )
	{
	case CONTAINER_TYPE_INT:
		return "CONTAINER_TYPE_INT";

	case CONTAINER_TYPE_UINT:
		return "CONTAINER_TYPE_UINT";

	case CONTAINER_TYPE_STRING:
		return "CONTAINER_TYPE_STRING";

	case CONTAINER_TYPE_NONE:
	default:
		// nicht bekannter / spezialisierter Typ ... exception werfen oder assert()
		// ...
		throw std::exception();
	}
}

class ValueContainerBase
{
public:
	virtual ~ValueContainerBase() {}

	inline ContainerType valueType() const { return m_type; }
	
	void printValue( std::ostream& toHere ) const
	{
		printValue_impl( toHere );
	}

protected:
	explicit ValueContainerBase( ContainerType type ) : m_type( type ) {}

private:
	const ContainerType m_type;
	
	virtual void printValue_impl( std::ostream& toHere ) const = 0;
};

template < typename ValueType >
ContainerType determineValueTypeEnum()
{
	// Sollte eigentlich nie verwendet werden. Nur die Spezialisierungen weiter unten.
	return CONTAINER_TYPE_NONE;
}

template <>
ContainerType determineValueTypeEnum< std::string >()
{
	return CONTAINER_TYPE_STRING;
}

template <>
ContainerType determineValueTypeEnum< unsigned int >()
{
	return CONTAINER_TYPE_UINT;
}

template <>
ContainerType determineValueTypeEnum< int >()
{
	return CONTAINER_TYPE_INT;
}

// Spezialisierungen für weitere Typen folgen ...
// ...



template < typename ValueType >
void printValueFunction( const ValueType& value, ContainerType valueTypeEnum, std::ostream& toHere  )
{
	toHere << "This value is of type " << containerTypeEnumToString( valueTypeEnum ) << ", and the value is "
		<< value << "\n";

	// damit das funktioniert, muß eventuell noch operator << für std::ostream& und den jeweiligen typ überladen werden
}

template < typename ValueType >
class ValueContainer : public ValueContainerBase
{
public:
	explicit ValueContainer( const ValueType& value ) : ValueContainerBase( determineValueTypeEnum< ValueType > () ), m_value( value ) {}

private:
	const ValueType m_value;
	
	virtual void printValue_impl( std::ostream& toHere ) const
	{
		printValueFunction< ValueType > ( m_value, valueType(), toHere );
	}
};


int main()
{
	const ValueContainer< int > a( 5 );
	const ValueContainer< std::string > b( "Meep meep!" );

	a.printValue( std::cout );
	b.printValue( std::cout );

       // Du könntest nun sogar Instanzen von ValueContainer< Typ > mit new erstellen und dann
	// als Pointer auf die ValueContainerBase-Klasse speichern und printValue() polymorphisch
	// an diesen Pointern aufrufen. 
}

Hat natürlich den Nachteil, daß du

  • alle möglichen Typen, die für dein Szenario in Frage kommen, schon vorher kennen mußt
  • pro Typ eine Spezialisierung des determineValueTypeEnum()-Templates schreiben mußt und eventuell auch noch für eine Überladung von operator << für std::ostream und deinen Typ.

Aber eleganter wäre vielleicht tatsächlich boost::any, wie von badday vorgeschlagen. Ich habe es selbst ebenfalls noch nie eingesetzt, aber alle boost-Komponenten, die ich bisher verwendet habe, empfand ich persönlich als sehr angenehm zu benutzen und von hoher Qualität. :)
 
Vielen Dank Euch erstmal!

Die Boost Geschichte werde ich mir nochmal genauer anschauen, kenne any oder variant ebenfalls nicht.

Ein Kollege meinte, man könne das auch mit virtuellen funktionen hinbekommen, dafür müsste aber für jeden Eintrag ein Objekt erzeugt werden und das ist unschön, weil langsam.

@Datentypen: es liegen nur strings, ints und doubles vor.

Vielen Dank!
 

Ähnliche Themen

Zurück
Oben