Sign in to follow this  

[C++] Container for any type

This topic is 4305 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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?

Share this post


Link to post
Share on other sites
/* 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

Share this post


Link to post
Share on other sites
Guest 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.

Share this post


Link to post
Share on other sites
@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?

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
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.

Share this post


Link to post
Share on other sites
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...

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
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..

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
I've got another short question: Is this a good way of implementing an unsafe cast for CAnyTypeWrapper?

template<typename TType> TType unsafe_cast(CAnyTypeWrapper &obj) 
{
return *((TType*)((CAnyType<TType>*)obj.object->get()));
}



To allow access to object you'd have to make it a friend function of CAnyTypeWrapper. A call would look like that:
CAnyTypeWrapper any(5);
int i = unsafe_cast<int>(any);

Share this post


Link to post
Share on other sites

This topic is 4305 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this