C++ Template Random Access Operator

Started by
5 comments, last by Antheus 15 years, 9 months ago
I am in the process of creating a generic data class, to help me simplify the parsing of object attributes from text/xml documents. This class will be string based and I will need to be able to cast it to/from pretty much any basic type i.e. char *, bool, int and float. Here's a rough first pass:

class CGeneric
{
	public:
		
		CGeneric(const char *pString)
		{
			strncpy(m_string, pString, sizeof(m_string) - 1);
		}

		operator const char * () const
		{
			return((const char *)m_string);
		}

		operator int () const
		{
			return((int)atoi(m_string));
		}

		operator float () const
		{
			return((float)atof(m_string));
		}

		void operator = (const char *pString)
		{
			strncpy(m_string, pString, sizeof(m_string) - 1);
		}

		void operator = (int value)
		{
			sprintf(m_string, "%d", value);
		}

		void operator = (float value)
		{
			sprintf(m_string, "%f", value);
		}

	private:

		char	m_string[32];
};

void TestFunction()
{
	CGeneric	genericData("12.4");

	int	myInt = genericData;	// myInt = 12

	float	myFloat = genericData;	// myFloat = 12.4
}







This works pretty nicely, but I would really like to add array/random access functionality, which would work something like this:

struct CVector3
{
	float	x, y, z;
};

void TestFunction()
{
	CGeneric	genericData("10, 15, -12");

	CVector3	pos;

	pos.x	= genericData<float>[0];
	pos.y	= genericData<float>[1];
	pos.z	= genericData<float>[2];
}







Unfortunately I'm having trouble implementing the operator. It isn't possible to create separate functions for each type (I get a compile error because the functions differ only by return type), so the only other way I can see to do this is to declare a template function like this:

template <typename TYPE> TYPE operator [] (int i)
{
	return((TYPE)0); // Worry about implementation details later.
}







This compiles, but I do not know how I can then tell the compiler what type I want to access. I tried genericData<int>[0], <int>genericData[0] and genericData[0]<int>, but all produced compile errors. Have I hit a dead end or is there a way to specify the type? If I have hit a dead end, does anyone have any suggestions? Thanks in advance.
Advertisement
Why not have two classes, the first class (CGenerics?) contains a vector of CGeneric classes. This class also splits up the comma-based input to each CGeneric instance in its constructor. The [] operator then returns a CGeneric class reference, which uses the type conversion operators you already have.

Also, I feel like I should point out that making m_string a fixed size isn't very nice. What's wrong with std::string, and std::stringstream for the conversions? atoi and sprintf aren't very C++ either. atoi isn't even standard C or C++ I believe. strtol, etc. is.
Million-to-one chances occur nine times out of ten!
Quote:Why not have two classes, the first class (CGenerics?) contains a vector of CGeneric classes. This class also splits up the comma-based input to each CGeneric instance in its constructor. The [] operator then returns a CGeneric class reference, which uses the type conversion operators you already have.


Thanks, I have considered this, but I would ideally like to keep it to a single generic object. I have a system for binding attributes, and want to be able to pass a single parameter when setting an object attribute, without caring whether it's a list or not. Then I can let the object worry about the finer details. Of course if I can't find another solution, then yours may be the best approach.

Quote:Also, I feel like I should point out that making m_string a fixed size isn't very nice. What's wrong with std::string, and std::stringstream for the conversions? atoi and sprintf aren't very C++ either. atoi isn't even standard C or C++ I believe. strtol, etc. is.


Don't worry, I'm well aware of these issues and I plan to fix them once the design is complete. I just wanted to get something up and running first.
Use this:
		CGeneric operator[](const unsigned int index) const		{			unsigned int c = 0;			unsigned int i;			for (i = 0; m_string; ++ i){				if (c == index)					return CGeneric(&m_string);				if (m_string == ',')					++ c;			}			return CGeneric("");		}


This does conflict with your operator:
		operator const char * () const		{			return((const char *)m_string);		}


But IMO I'd get rid of the char* for string and replace them with std::string's. That will solve it.
Thanks guys, I had a brain wave just after reading Mike nl's reply, and yeah, it's pretty much the same as the solution proposed by eq. Seems pretty obvious in hindsight, and works nicely, thanks again.
EDIT:

second thought, just do something like this:

#include <iostream>#include <vector>#include <sstream>#include <string>using namespace std;class CGeneric{    public:                explicit CGeneric(const string& str) : str_(str) { }                template <typename Type>        operator Type () const        {            Type var;            istringstream iss(str_);            iss >> var;            if(!iss.bad() && !iss.fail()) { /* throw exception*/ }            return var;        }        template <typename Type>        CGeneric& operator = (const Type& var)        {            ostringstream oss;            oss << var;            str_ = oss.str();            return *this;        }            private:        string str_;};struct CVector3{    float x, y, z;};istream& operator >> (istream& is, CVector3& vec){    static string ignore_comma;    return is >> vec.x >> ignore_comma >> vec.y >> ignore_comma >> vec.z;}ostream& operator << (ostream& os, const CVector3& vec){    return os << vec.x << ", " << vec.y << ", " << vec.z;}void main(int argc, char** argv){    CGeneric genericData("12.4");    int myInt = genericData;    // myInt = 12    genericData = "2";    float myFloat = genericData;    // myFloat = 2    cout << myInt << ' ' << myFloat << endl;    genericData = "12, 12, 12";        CVector3 foo = genericData;    cout << foo << endl;    cin.get();}


useful links:
http://www.gotw.ca/publications/mill19.htm
http://www.boost.org/doc/libs/1_35_0/libs/conversion/lexical_cast.htm



[Edited by - godecho on June 27, 2008 8:41:25 AM]
Quote:Original post by pauls_1979
I am in the process of creating a generic data class, to help me simplify the parsing of object attributes from text/xml documents.


What is wrong with established canonical representation of XML DOM?

You have Node and Attributes, each of which is a sequence. Elements of the sequence are simple values.

Vector in XML looks like this:
<vector x="1" y="2" z="3"/>or<vector>  <x>1</x>  <y>2</y>  <z>3</z></vector>or<vector>  <coord name="x" value="1"/>  <coord name="y" value="2"/>  <coord name="z" value="3"/></vector>or perhaps some other variation


The above format is adequate and suitable for representation of arbitrary data type.

XML has its uses, but unfortunately it gets often abused, throwing away the benefits, and just keeping cumbersome syntax.

XML is a good choice when you will be dealing with a lot of context-free transformations. It is absolutely wrong choice for compact representation of data, since XML is by definition verbose.


The canonical entity serialization for arbitrary classes looks something like this:
// First of the above formatsstruct Foo {  Foo(const xml::dom::Node & node) {    x = node.getAttribute("x").as<double>();    y = node.getAttribute("y").as<double>();    y = node.getAttribute("z").as<double>();  }};

This topic is closed to new replies.

Advertisement