I\'ve got an interesting problem that\'s cropped up in a sort of pass based compiler of mine. Each pass knows nothing of other passes, and a common object is passed down the
As ergosys said already, std::map is the obvious solution to your problem. But I can see you concerns with RTTI (and the associated bloat). As a matter of fact, an "any" value container does not need RTTI to work. It is sufficient to provide a mapping between a type and an unique identifier. Here is a simple class that provides this mapping:
#include
#include
class typeinfo
{
private:
typeinfo(const typeinfo&);
void operator = (const typeinfo&);
protected:
typeinfo(){}
public:
bool operator != (const typeinfo &o) const { return this != &o; }
bool operator == (const typeinfo &o) const { return this == &o; }
template
static const typeinfo & get()
{
static struct _ti : public typeinfo {} _inst;
return _inst;
}
};
typeinfo::get
returns a reference to a simple, stateless singleton which allows comparisions.
This singleton is created only for types T where typeinfo::get< T >() is issued anywhere in the program.
Now we are using this to implement a top type we call value
. value
is a holder for a value_box
which actually contains the data:
class value_box
{
public:
// returns the typeinfo of the most derived object
virtual const typeinfo& type() const =0;
virtual ~value_box(){}
};
template
class value_box_impl : public value_box
{
private:
friend class value;
T m_val;
value_box_impl(const T &t) : m_val(t) {}
virtual const typeinfo& type() const
{
return typeinfo::get< T >();
}
};
// specialization for void.
template<>
class value_box_impl : public value_box
{
private:
friend class value_box;
virtual const typeinfo& type() const
{
return typeinfo::get< void >();
}
// This is an optimization to avoid heap pressure for the
// allocation of stateless value_box_impl instances:
void* operator new(size_t)
{
static value_box_impl inst;
return &inst;
}
void operator delete(void* d)
{
}
};
Here's the bad_value_cast exception:
class bad_value_cast : public std::runtime_error
{
public:
bad_value_cast(const char *w="") : std::runtime_error(w) {}
};
And here's value:
class value
{
private:
boost::shared_ptr m_value_box;
public:
// a default value contains 'void'
value() : m_value_box( new value_box_impl() ) {}
// embedd an object of type T.
template
value(const T &t) : m_value_box( new value_box_impl(t) ) {}
// get the typeinfo of the embedded object
const typeinfo & type() const { return m_value_box->type(); }
// convenience type to simplify overloading on return values
template struct arg{};
template
T convert(arg) const
{
if (type() != typeinfo::get())
throw bad_value_cast();
// this is safe now
value_box_impl *impl=
static_cast*>(m_value_box.get());
return impl->m_val;
}
void convert(arg) const
{
if (type() != typeinfo::get())
throw bad_value_cast();
}
};
The convenient casting syntax:
template
T value_cast(const value &v)
{
return v.convert(value::arg());
}
And that's it. Here is how it looks like:
#include
#include
The nice thing about having you own implementation of any
is, that you can very easily tailor it to the features you actually need, which is quite tedious with boost::any. For example, there are few requirements on the types that value can store: they need to be copy-constructible and have a public destructor. What if all types you use have an operator<<(ostream&,T) and you want a way to print your dictionaries? Just add a to_stream method to box and overload operator<< for value and you can write:
std::cout << v["zero"] << std::endl;
std::cout << v["pi"] << std::endl;
std::cout << v["password"] << std::endl;
Here's a pastebin with the above, should compile out of the box with g++/boost: http://pastebin.com/v0nJwVLW
EDIT: Added an optimization to avoid the allocation of box_impl< void > from the heap: http://pastebin.com/pqA5JXhA