views:

132

answers:

3

I have a Map std::map<std::string, boost::any>, which comes from the boost::program_options package. Now I would like to print the content of that map:

for(po::variables_map::const_iterator it = vm.begin(); it != vm.end(); ++it) {
  std::cerr << it->first << ": " << it->second << std::endl;
}

Unfortunately, that is not possible because boost::any doesn't have an operator<< defined.

What is the easiest way to print that map?

I could define my own output operator for any that automatically tries to cast each any to an int, then double, then string, etc., each time ignoring errors and trying to cast until the cast is successful and I can print as the specified type.

But there should be an easier method in Boost? I'd need something like a reverse lexical_cast...

A: 

I think you have to cover each possible case of objects you have to print... Or use boost::variant.

EDIT: Sorry, I thought I shall write WHY.

The reason why I think that is because, looking at any source code, it seems to rely on the fact that YOU provide the types when inserting and getting data. When you insert, data is automatically detected by the compiler, so you don't have to specify it. But when you get the data, you shall use any_cast, because you're not sure of the data type you're getting.

If it worked in a different way and data type was sure, I think that would be no need for any_cast :)

Instead, variant have a limited set of possible data types, and this information is somewhat registered, giving you the ability to iterate in a generic way a variant container.

If you need this kind of manipulation - iterating a generic set of values - I think you shall use variant.

AkiRoss
+2  A: 

Unfortunately, with any the only way is to use the type() method to determine what is contained within any, then cast it with any_cast. Obviously you must have RTTI enabled, but you probably already do if you're using any:

for(po::variables_map::const_iterator it = vm.begin(); it != vm.end(); ++it) {
  if(typeid(float) == it->second.type()) {
      std::cerr << it->first << ": " << any_cast<float>(it->second) << std::endl;
  }
  else if(typeid(int) == it->second.type()) {
      std::cerr << it->first << ": " << any_cast<int>(it->second) << std::endl;
  }
  ...
}
manifest
+2  A: 

You could use boost::spirit::hold_any instead. It's defined here:

#include <boost/spirit/home/support/detail/hold_any.hpp>

and is fully compatible with boost::any. This class has two differences if compared to boost::any:

  • it utilizes the small object optimization idiom and a couple of other optimization tricks, making spirit::hold_any smaller and faster than boost::any
  • it has the streaming operators (operator<<() and operator>>()) defined, allowing to input and output a spirit::hold_any seemlessly.

The only limitation is that you can't input into an empty spirit::hold_any, but it needs to be holding a (possibly default constructed) instance of the type which is expected from the input.

hkaiser
+1 This is a very interesting little gem. There should be no reason for any to not be able to do this. Wonder why hold_any is only part of spirit because it takes care of a lot of boilerplate if your spitting any's out to streams or reading them in? Nice find
manifest
It's part of Spirit only mainly because I don't have neither the time nor the energy to make it replace `boost::any`.
hkaiser