The C++ Boost Libraries Part 6 – boost::any

In C++ if you have a variable that you say is of type “Person” (for instance), you can be fairly certain (more or less) that it always actually contains a Person (or perhaps a subclass of Person. If you have a container of Persons, then you know (more or less) that every member is also a Person (or a subclass).

This is all very good, prevents a lot of runtime errors, and generally makes C++ a great language if you care about correctness. But sometimes, very rarely, you actually want to store a whole bunch of messy, unrelated types in a container without trying to ram them into some sort of class hierarchy. Parsers are a good example of this. It is often convenient just to chuck tokens of various types into a data structure for later processing without worrying too much about the specific type (string, int, float, etc).

boost::any is a small class that can hold values from almost any type, designed for just such messy applications. Using boost::any is very simple:

1
2
boost::any a1 = std::string("Moose");
boost::any a2 = 6;

Of course, getting the values back again is a little harder.

1
2
3
4
5
6
7
8
9
try
{
	std::string v1 = boost::any_cast< std::string >(a1); // this works, a1 is a string
	std::string v2 = boost::any_cast< std::string >(a2); // nope, will throw an exception at runtime
}
catch ( const boost::bad_any_cast& e )
{
	// tried to any_cast into something that wouldn't go
}

Of course, you can query a boost::any for the typeid of the stored object. Just don’t do it when Scott Meyers is in the vicinity.

1
2
3
4
5
std::string v;
if ( a1.type() == typeid(std::string) )
{
	v = boost::any_cast< std::string >( a1 ); // this should never throw, since we checked first
}

A single boost::any is perhaps not that useful, but a container of them can store almost anything we want:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
typedef std::vector< boost::any > AnyVector;
AnyVector values;
 
values.push_back( 5 );
values.push_back( std::string("Hello") );
values.push_back( 5.3 );
 
try 
{
	for ( AnyVector::const_iterator p = values.begin();
			p != values.end();
			++p )
	{
		if ( p->type() == typeid(int) )
			cout << "Int = " << any_cast<int>(*p) << endl;
		else if ( p->type() == typeid(std::string) )
			cout << "String = " << any_cast<string>(*p) << endl;
		else 
		{
			cout << "Unhandled type: " << p->type().name() << endl;
		}
	}
} 
catch ( const boost::bad_any_cast &e )
{
	cout << "Bad any_cast<>" << e.what() << endl;
}

Any type that you put into a boost::any must be copy constructable (the any makes a copy, not a reference). You also have to make sure that its destructor doesn’t throw (but of course you do that anyway!)

Although I wouldn’t recommend boost::any for everyday use, it does come into its own when the only alternative is a huge class structure or (even worse) void *s.


8 thoughts on “The C++ Boost Libraries Part 6 – boost::any

  1. Stu

    Actually it reminded me more of Visual Basic programming than C…

    Oh god, did I just out myself as having some VB knowledge…

  2. rajendra

    I got problem in casting boost::any to float.
    Will you please help me out?
    the program crashes with bad cast exception.
    float Value = boost::any_cast(Default);
    where Default is boost::any type.
    Thank you in advance

  3. Andrew

    Rajendra, is Default the name of a variable? I can’t tell without looking at the rest of your code, but I would guess that Default has not been assigned a float. What happens if you print out the Default.type().name() ?

Comments are closed.