[C++] Container for any type

Started by
9 comments, last by kloffy 18 years, 2 months ago
I've been playing around with templates lately and I ran into a problem. Lets say I wanted a Container to hold any type, like boost::any. One way to do this would be like that:
/* Abstract base class for my container */
class CAnyBase
{
public:
	virtual void* get()=0;
	virtual const type_info& type()=0;
};

/* Template class to hold the object */
template<typename TType>
class CAnyType : public CAnyBase
{
public:
	CAnyType(TType _object){ object = _object;}
	~CAnyType(){}

	virtual void* get() { return &object; }
	virtual const type_info& type() { return typeid(TType); }
private:
	TType object;
};

/* Wrapper class */
class CAnyTypeWrapper
{
public:
	template<typename TType> CAnyTypeWrapper(TType _object){ object = new CAnyType<TType>(_object);}
	~CAnyTypeWrapper(){}

	template<typename TType> bool put(TType& temp) { if(typeid(TType)==object->type()) { temp = *((TType*)object->get()); return 1;} return 0; }

private:
	CAnyBase* object;
};




Now this works pretty nicely. You can do things like:
string str = "hi";

vector<CAnyTypeWrapper*> anyvector;
anyvector.push_back(new CAnyTypeWrapper(str)); //StdStrings 
anyvector.push_back(new CAnyTypeWrapper(1));   //Integers
anyvector.push_back(new CAnyTypeWrapper('c')); //Chars etc.
However, if I wanted to overload the stream operators to work with my CAnyTypeWrapper for example, the only way I can come up with is this:

	friend istream& operator >>(istream &is,CAnyTypeWrapper &obj)
	{
		if(typeid(int)		== obj.object->type()) { is >> *((int*)obj.object->get());		}
		else if(typeid(float)	== obj.object->type()) { is >> *((float*)obj.object->get());		}
		else if(typeid(double)	== obj.object->type()) { is >> *((double*)obj.object->get());		}
		else if(typeid(char)	== obj.object->type()) { is >> *((char*)obj.object->get());		}
		else if(typeid(bool)	== obj.object->type()) { is >> *((bool*)obj.object->get());		}

		else if(typeid(int*)	== obj.object->type()) { is >> *((int*)obj.object->get());		}
		else if(typeid(float*)	== obj.object->type()) { is >> *((float*)obj.object->get());		}
		else if(typeid(double*)	== obj.object->type()) { is >> *((double*)obj.object->get());		}
		else if(typeid(char*)	== obj.object->type()) { is >> *((char*)obj.object->get());		}
		else if(typeid(bool*)	== obj.object->type()) { is >> *((bool*)obj.object->get());		}

		else if(typeid(string)	== obj.object->type()) { is >> *((string*)obj.object->get());		}

		//And so on...

		return is;
	}
	friend ostream& operator <<(ostream &os,const CAnyTypeWrapper &obj)
	{
		if(typeid(int)		== obj.object->type()) { os << *((int*)obj.object->get());		}
		else if(typeid(float)	== obj.object->type()) { os << *((float*)obj.object->get());		}
		else if(typeid(double)	== obj.object->type()) { os << *((double*)obj.object->get());		}
		else if(typeid(char)	== obj.object->type()) { os << *((char*)obj.object->get());		}
		else if(typeid(bool)	== obj.object->type()) { os << *((bool*)obj.object->get());		}

		else if(typeid(int*)	== obj.object->type()) { os << *((int*)obj.object->get());		}
		else if(typeid(float*)	== obj.object->type()) { os << *((float*)obj.object->get());		}
		else if(typeid(double*)	== obj.object->type()) { os << *((double*)obj.object->get());		}
		else if(typeid(char*)	== obj.object->type()) { os << *((char*)obj.object->get());		}
		else if(typeid(bool*)	== obj.object->type()) { os << *((bool*)obj.object->get());		}

		else if(typeid(string)	== obj.object->type()) { os << *((string*)obj.object->get());		}

		//And so on...

		return os;
	}



Which, of course, isn't very nice. Is there a better way to do this?
Advertisement
Why do you need to put different-typed things in the same container?
/* Abstract base class for my container */class CAnyBase{public:	virtual void* get()=0;	virtual const type_info& type()=0;	virtual std::istream & read(std::istream &)=0;	virtual std::ostream & write(std::ostream &)=0;};/* Template class to hold the object */template<typename TType>class CAnyType : public CAnyBase{public:	CAnyType(TType _object){ object = _object;}	~CAnyType(){}	virtual void* get() { return &object; }	virtual const type_info& type() { return typeid(TType); }	virtual std::istream & read(std::istream & reader) { return reader >> object; }	virtual std::ostream & write(std::ostream & writer) { return writer << object; }private:	TType object;};/* Wrapper class */class CAnyTypeWrapper{public:	template<typename TType> CAnyTypeWrapper(TType _object){ object = new CAnyType<TType>(_object);}	~CAnyTypeWrapper(){}	template<typename TType> bool put(TType& temp) { if(typeid(TType)==object->type()) { temp = *((TType*)object->get()); return 1;} return 0; }	friend std::istream & operator>>(std::istream & reader, CAnyTypeWrapper & obj) { return obj.object->read(reader); }	friend std::ostream & operator<<(std::ostream & writer, CAnyTypeWrapper & obj) { return obj.object->write(writer); }private:	CAnyBase* object;};

I prefer boost::any to your solution.

Σnigma
Make stream operators for CAnyBase and just call "is >> obj.object;" in the function you have now. CAnyBase's stream operator should call a new virtual function "getFromStream" declared at CAnyBase and defined at CAnyType. In CAnyType the getFromStream function simply calls is >> object; Here the type of object is known and it calls directly the correct >>-operator.
Aww, I was slow.
@RDragon1:
Actually I'm not really interested in the container. What I really want is a functor class that can hold different types of functions. I ran into the same problem though - and the container is the easier example.

@Enigma:
As far as I can tell boost::any isn't really that different. Why do you think it's better?
Quote:Original post by kloffy
@Enigma:
As far as I can tell boost::any isn't really that different. Why do you think it's better?
At least it's semi-standard so if everyone uses boost::any instead of custom solutions, people don't have to relearn the same feature every time.

Quote:Original post by Anonymous Poster
Make stream operators for CAnyBase and just call "is >> obj.object;" in the function you have now. CAnyBase's stream operator should call a new virtual function "getFromStream" declared at CAnyBase and defined at CAnyType. In CAnyType the getFromStream function simply calls is >> object; Here the type of object is known and it calls directly the correct >>-operator.

That sounds good, I'll try it...

Btw.: I doubt anybody will have to relearn the same feature, because I'm not planning to release this. It's just for learning purposes on my behalf...
Quote:Original post by kloffy
That sounds good, I'll try it...
It's roughly the same as what Enigma gave in code. Only he calls the virtual function directly from CAnyTypeWrapper's stream operator..
Quote:Original post by Anonymous Poster
It's roughly the same as what Enigma gave in code. Only he calls the virtual function directly from CAnyTypeWrapper's stream operator..

Oh, I missed that. I thought he only quoted the code to say he prefers boost::any. Thanks both of you for your help!

This topic is closed to new replies.

Advertisement