Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Boost program options iterate over variables_map

po::options_description desc("This are the options that are available");
    desc.add_options()("help", "print help")(
        "deer", po::value<uint32_t>(), "set how many deer you want")(
        "rating", po::value<uint32_t>(), "how good ?")(
        "name", po::value<std::string>(), "and your name is ... ?");

po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);

in a following section of the code I tried to iterate over vm

for (const auto& it : vm) {
      std::cout << it.first.c_str() << " "
                << it.second.as<it.pair::second_type>() << "\n";
    }

The main point here is that vm contains keys of the same type, but values with different types, in this example I have uint32_t mixed with a std::string.

How I can iterate over this kind of containers ? I would like to avoid a verbose approach so I'm trying to just iterate over this data structure.

EDIT:

I forgot to write this down, but obviously

namespace po = boost::program_options;
like image 577
user2485710 Avatar asked Jan 08 '14 23:01

user2485710


2 Answers

boost variable_map use boost::any as the value so you can try to use boost::any_cast<T> to find out the type. perhaps something like this

for (const auto& it : vm) {
  std::cout << it.first.c_str() << " ";
  auto& value = it.second.value();
  if (auto v = boost::any_cast<uint32_t>(&value))
    std::cout << *v;
  else if (auto v = boost::any_cast<std::string>(&value))
    std::cout << *v;
  else
    std::cout << "error";
}
like image 51
yngccc Avatar answered Nov 08 '22 09:11

yngccc


boost::program_options::variable_map is essentially an std::map<std::string, boost::any>, which means it uses type erasure to store the values. Because the original type is lost, there's no way to extract it without casting it to the correct type. You could implement a second map that contains the option name as key, and the extraction function as value, allowing you to dispatch the value to the appropriate extractor at runtime.

using extractor = std::map<std::string, void(*)(boost::variable_value const&)>;

or

using extractor = std::map<std::string, 
                           std::function<void(boost::variable_value const&)>;

if your extractors are more complicated and will not convert to a simple function pointer. An example of an extractor that'll print a uint32_t is

auto extract_uint32_t = [](boost::variable_value const& v) {
                             std::cout << v.as<std::uint32_t>();
                        };

Then your loop would look like this:

for (const auto& it : vm) {
  std::cout << it.first.c_str() << " "
  extractor_obj[it.first](it.second) 
  std::cout << "\n";
}

Here's a live demo with some made up types, but it's close enough to your use case that you should be able to apply something similar.

like image 39
Praetorian Avatar answered Nov 08 '22 10:11

Praetorian