Presenting the Problem
There's no need to explain why heterogeneous containers are useful. Yet the current techniques of imitating heterogeneity aren't satisfactory. Take for example the
shared_ptr approach. Although this is safer than using raw pointers, it still forces you to use the cumbersome pointer syntax. Worse yet, it only allows you to store objects that belong to the same class hierarchy. What if you want to store unrelated datatypes such as
int, std::string, and
CDialog in the same container? Theoretically, you can use
tuples for this purpose. However, tuples aren't real containersthey have a statically fixed size and they hold, at most, ten elements. Does that mean you're still stuck with the burdensome container of pointers approach? Not necessarily. Class
boost::any, which is part of the
Boost.Any library, overcomes many of the limitations of traditional heterogeneous containers.
Anything Goes
boost::any allows for type-safe storage and the retrieval of arbitrary types. The main difference between this class and other indiscriminate types (like unions, variant_t, etc.) is that the caller must know the exact type of the value stored in boost::any in order to access it.
| Author's Note: Before you can use boost::any, you need to download the relevant boost libraries and install them. Note also that according to the Boost convention, class any and its related helper functions are declared in namespace boost. |
boost::any has a default constructor which creates an empty any object, and another non-explicit constructor which takes an object of an arbitrary type. The copy constructor and assignment operator enable you to copy and assign any type of object to any:
boost::any a;
a=10;
a=std::string("I'm a string");
boost::any a2(a);
You can store any object that meets the copy-constructible and assignable requirements in
any. To access the value stored in
any, use the
boost::any_cast function.
any_cast is similar to operator
dynamic_cast: its first argument is a type name, and the second is the actual
any object. If the type name matches the object's type,
any_cast returns a reference to the stored object. Otherwise, it throws a
boost::bad_any_cast exception:
try
{
std::string test=boost::any_cast<std::string> (a);
}
catch(boost::bad_any_cast & e)
{
cout<<"it's not a string!" <<endl;
}
any_cast also has a pointer version which doesn't throw. If the types of the target and the source don't match, a
NULL pointer is returned:
string *ptest=boost::any_cast<string> (&a);
if(ptest)
{
cout<<*ptest<<endl;
}
else
{
cout<<"it's not a string!" <<endl;
}
any grants access to the stored object only if you know its type. Since
any can contain at most one object at a time, assigning a new object to it overrides the previously stored value (and its type). The
any_cast operations above are successful because the last object assigned to
any was a string.