The C++ Boost Libraries Part 6 – boost::any
December 6, 2009 – 3:41 pmIn 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.
Related posts:
- A Better Boost Book Boost is a excellent resource for C++ programming, but suffers...
- Using Exceptions in C++ C++ is big – it has been said that any...
Related posts brought to you by Yet Another Related Posts Plugin.
5 Responses to “The C++ Boost Libraries Part 6 – boost::any”
Might as well go back to C programming with that abomination!
By Aza on Dec 6, 2009
Maybe, but at least it is a step up from void *.
By Andrew on Dec 6, 2009
Actually it reminded me more of Visual Basic programming than C…
Oh god, did I just out myself as having some VB knowledge…
By Stu on Dec 7, 2009
I always have horrible flashbacks to the COM VARIANT type when I see that thing. I’m sure it’s handy in some cases, but it still make me itch.
By James Wells on Dec 8, 2009
Yeh… I guess its at least better than void *….
By Chuck on Dec 16, 2009