devxlogo

Revisiting Heterogeneous Containers

Revisiting Heterogeneous Containers

ive years ago, I explained how to simulate heterogeneous containers by storing raw pointers to polymorphic objects in an STL container. C++ has come along way since: tuples now allow you store arbitrary types in one object. Alternatively, you can replace the raw pointers with shared_ptr. And yet, a heterogeneous container that is free from the hassles of pointer syntax and other arbitrary restrictions is still elusive. This month’s solution presents another technique for implementing heterogeneous containers using the Boost.Any library.


How to store objects of different types in an STL container without the restrictions and complexities of pointers?


Use the Boost.Any library to safely store objects of arbitrary datatypes in a container.

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 containers?they 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 (a);}catch(boost::bad_any_cast & e){ cout<<"it's not a string!" <

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 (&a);if(ptest){ cout<<*ptest<

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.

Creating a Heterogeneous Container
The following example uses a vector of any to store four unrelated objects:

using std::vector; using std::string;using std::make_pair;using std::for_each;int main(){ vector bag; bag.push_back(string());  bag.push_back(80); bag.push_back(myclass()); //store a pair bag.push_back(make_pair(string("PAGE_SIZE"), 1024)); for_each(bag.begin(), bag.end(), retrieve);}

bag really looks like a heterogeneous container! But how do you access the elements stored in it? First, access the vector's elements as usual, using iterators (as shown in the example) or the [] notation. Next, use the any_cast function to retrieve the value stored in each element:

using std::string;using std::map;using std::cout;using std::endl;void retrieve(boost::any & a){ if(string *pstr = boost::any_cast(&a)) {  cout<<"string is: "<< *pstr < (&a)) {  cout<<"int: "<< *pi <(&a)) {  cout<< "myclass: " < *pmap   =boost::any_cast >(&a)) {  cout<< "map: " << pmap->first <<" "    << pmap>second >>endl; } else {  cout<< "unknown type" << endl; }}

Unlike dynamic_cast, boost::any supports non-polymorphic objects and built-in datatypes as well. There's no need to worry about performance either?as opposed to dynamic_cast, boost::any_cast actually uses static_cast under the hood, so it's pretty efficient.

Additional Member Functions
any's destructor destroys the object stored in it. However, because the destructor calls delete and never delete[], you should wrap pointers to arrays in an appropriate smart pointer before storing them in any.

The empty() member function returns a Boolean value indicating whether the any object contains a value:

boost:any anybody;cout<

any objects. Finally, the type() member function returns a reference to std::type_info which you can use in a typeid expression:

if (typeid(any1.type())==typeid(int)){ int val=boost::any_cast (any1);}

Safe and Efficient
boost::any brings STL containers closer to the SmallTalk-style concept of heterogeneous containers, with minimal performance overhead and without compromising type-safety. Remember that boost::any is also useful in other frameworks that require indiscriminate types. What's still missing in standard C++ is a reflection library that safely reconstructs objects and their types from a collection.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist