Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

boost::program_options : iterating over and printing all options

I have recently started to use boost::program_options and found it to be highly convenient. That said, there is one thing missing that I was unable to code myself in a good way:

I would like to iterate over all options that have been collected in a boost::program_options::variables_map to output them on the screen. This should become a convenience function, that I can simply call to list all options that were set without the need to update the function when I add new options or for each program.

I know that I can check and output individual options, but as said above, this should become a general solution that is oblivious to the actual options. I further know that I can iterate over the contents of variables_map since it is simply an extended std::map. I could then check for the type containd in the stored boost::any variable and use .as<> to convert it back to the appropriate type. But this would mean coding a long switch block with one case for each type. And this doesn't look like good coding style to me.

So the question is, is there a better way to iterate over these options and output them?

like image 819
shiin Avatar asked Sep 03 '12 08:09

shiin


3 Answers

Since you are going to just print them out anyway you can grab original string representation when you parse. (likely there are compiler errors in the code, I ripped it out of my codebase and un-typedefed bunch of things)

std::vector<std::string> GetArgumentList(const std::vector<boost::program_options::option>& raw)
{
    std::vector<std::string> args;

    BOOST_FOREACH(const boost::program_options::option& option, raw)
    {
        if(option.unregistered) continue; // Skipping unknown options

        if(option.value.empty())
            args.push_back("--" + option.string_key));
        else
        {
            // this loses order of positional options
            BOOST_FOREACH(const std::string& value, option.value)
            {
                args.push_back("--" + option.string_key));
                args.push_back(value);
            }
        }
    }

    return args;
}

Usage:

boost::program_options::parsed_options parsed = boost::program_options::command_line_parser( ...

std::vector<std::string> arguments = GetArgumentList(parsed.options);
// print
like image 131
Eugene Avatar answered Oct 20 '22 01:10

Eugene


As @Rost previously mentioned, Visitor pattern is a good choice here. To use it with PO you need to use notifiers for your options in such a way that if option is passed notifier will fill an entry in your set of boost::variant values. The set should be stored separately. After that you could iterate over your set and automatically process actions (i.e. print) on them using boost::apply_visitor.

For visitors, inherit from boost::static_visitor<>

Actually, I made Visitor and generic approach use more broad.

I created a class MyOption that holds description, boost::variant for value and other options like implicit, default and so on. I fill a vector of objects of the type MyOption in the same way like PO do for their options (see boost::po::options_add()) via templates. In the moment of passing std::string() or double() for boosts::variant initialization you fill type of the value and other things like default, implicit.

After that I used Visitor pattern to fill boost::po::options_description container since boost::po needs its own structures to parse input command line. During the filling I set notifyer for each option - if it will be passed boost::po will automatically fill my original object of MyOption.

Next you need to execute po::parse and po::notify. After that you will be able to use already filled std::vector<MyOption*> via Visitor pattern since it holds boost::variant inside.

What is good about all of this - you have to write your option type only once in the code - when filling your std::vector<MyOption*>.

PS. if using this approach you will face a problem of setting notifyer for an option with no value, refer to this topic to get a solution: boost-program-options: notifier for options with no value

PS2. Example of code:

std::vector<MyOptionDef> options;
OptionsEasyAdd(options)
  ("opt1", double(), "description1")
  ("opt2", std::string(), "description2")
...
;
po::options_descripton boost_descriptions;
AddDescriptionAndNotifyerForBoostVisitor add_decr_visitor(boost_descriptions);
// here all notifiers will be set automatically for correct work with each options' value type
for_each(options.begin(), options.end(), boost::apply_visitor(add_descr_visitor));  
like image 7
Riga Avatar answered Nov 04 '22 10:11

Riga


It's a good case to use Visitor pattern. Unfortunately boost::any doesn't support Visitor pattern like boost::variant does. Nevertheless there are some 3rd party approaches.

Another possible idea is to use RTTI: create map of type_info of known types mapped to type handler functor.

like image 5
Rost Avatar answered Nov 04 '22 12:11

Rost