Alp Mestanogullari's Blog

Can I haz teh boost::any C++ class plz ?

Posted in Uncategorized by alpmestan on 2009/10/18

Welcome,

This blog post will be about rewriting the boost::any class. For those of you who don’t know it, it lets you assign a value of any type to your boost::any instance, like in the following code :

boost::any a = 42;
a = "C++";
a = 3.14;
// and so on

First, ok, we must have templates somewhere, since there isn’t any base class of all the possible types, even less when we deal with primitive types. So, our any cass must hold a value than actually can hold any kind of stuffs… Some value_type template class then ! But wait, we must be able to store value_type<int>, value_type<std::string> and so on ! OK, fortunately, type erasure will save us here. We’ll create a base class value_base and then make a template class value_type inherit it.

class value_base
{
	public:
	virtual ~value_base() { }
};

template <class T>
class value_type : public value_base
{
	T t;
	
	public:
	value_type(const T& t_) : t(t_) { }
};

Thus, we can write the any class this way :

class any
{
	value_base* v;
	
	public:
	any() : v(0) { }
	
	template <class value_t>
	any(const value_t& v_) : v(new value_type<value_t>(v_)) { }
	
	~any() { delete v; }
};

Ok, now we miss a copy constructor and an overload for the “=” operator.
We must first add a cloning function in value_base (pure virtual) and value_type for being able to write a good copy constructor.

class value_base
{
  	public:
  	virtual ~value_base() { }
  	virtual value_base* clone() const = 0; /* new */
};

template <class T>
class value_type : public value_base
{
	T t;
	
	public:
	value_type(const T& t_) : t(t_) { }
	value_base* clone() const /* new */
	{
		return new value_type(t);
	}
};

And now, any(const any&) and any& operator=(const any&) can be implemented ;)

class any
{
	value_base* v;
	
	public:
	any() : v(0) { }
	
	template <class value_t>
	any(const value_t& v_) : v(new value_type<value_t>(v_)) { }

        /* new */
	any(any const & other) : v(other.v ? other.v->clone() : 0) {}

        /* new */
	any& operator=(const any& other)
  	{
  		if(&other != this)
  		{
  			any copy(other);
  			swap(copy);
  		}
    	        return *this;
  	}

        /* new */
  	void swap(any& other)
  	{
    	        std::swap(v, other.v);
  	}

	~any() { delete v; }
};

And you’re done !

For those of you who either already knew any or just read a bit its documentation, you may be interested in the any cast functions, that let you get back a value from an any instance. Here is the code (it requires very little modifications to our previous code).

class any;

template <class T>
T any_cast(any& a); // declaration for being able to make classes friend of it 		
		
// value_type template class modification
template <class T>
class value_type : public value_base
{
  	friend T any_cast<>(any& a);
  	// ...
};

// any class modification
class any
{
  	template <class T>
  	friend T any_cast(any& a);
  	// ...
};

// bad_any_cast exception class
class bad_any_cast : public std::exception
{
  	public:
  	const char* what() const throw()
  	{
    	return "Bad any_cast exception";
  	}
};

template <class T>
T any_cast(any& a)
{
  	value_type<T>* v = dynamic_cast<value_type<T>*>(a.v);
  	if(v == 0)
	{
		throw bad_any_cast();
	}
  	else
	{
		return v->t;
	}
}

I haven’t implemented all the cast functions. The ones with constant reference, pointer, and constant pointer are left as exercise for the reader.

Here is a code using all the things we’ve done so far.

int main()
{
  	any a = 42;
  	any b = 'c';
  	std::cout << "[1] a=" << any_cast<int>(a) << " b='" << any_cast<char>(b) << "'" << std::endl;
  	a.swap(b);
  	std::cout << "[2] a='" << any_cast<char>(a) << "' b=" << any_cast<int>(b) << std::endl;
  	try
	{
		std::string s = any_cast<std::string>(b);
	}
  	catch(const std::exception& e)
	{
		std::cout << "[3] " << e.what() << std::endl;
	}
  	any c(a);
  	std::cout << "[4] c='" << any_cast<char>(c) << "'" << std::endl;
  	return 0;
}
/* 
alp@mestan:~/cpp$ g++ -o te3 type_erasure3.cpp
alp@mestan:~/cpp$ ./te3
[1] a=42 b='c'
[2] a='c' b=42	
[3] Bad any_cast exception
[4] c='c'
*/

Enjoy ;)

Tagged with: , ,
Follow

Get every new post delivered to your Inbox.

Join 251 other followers