It'sPete It'sPete - 1 year ago 102
C++ Question

boost::program_options positional options

I have a positional option (a file name) and I want it to be the very last option. Basically, the user can pass in a bunch of stuff on the command line, and also use -F for the file name. However, I want the user to also have the ability to just place the file name at the end.

For example

./program --var 3 /path/to/file

The code I currently have implemented allows the caller to place the file name wherever in the command line. Is there anyway to force the positional arguments to always come after the "regular" ones?

Here's how I set-up the positional argument:

pos_opts_desc.add("filename", -1);

And to parse the command line:

command_line_parser(argc, argv).options(opts_desc).postional(pos_opts_desc).run(),

Thanks in advance for the help.

Edited to add:

I'm perfectly OK with -F being specified anywhere in the command line. However, if the setting was done via the positional option, I want to ensure that the positional option is at the very end.

Answer Source

The run() member function gives you back an instance of type parsed_options. The simple usage is to never actually look at this object and pass it directly into store(), as in your example:

    po::command_line_parser(argc, argv).options(opts_desc).postional(pos_opts_desc).run(),

But we can hold onto it and examine its contents:

auto parsed = po::command_line_parser(argc, argv)
po::store(parsed, opts_var_map);

The parsed_options class has a member options which has an ordered list of all the options (unlike the variable map, which is ordered by option name - since it's a std::map). So you can look up the "filename" argument and check its position_key member. We want either: position_key == -1 (which means it provided with -F) or position_key == 0 and it being the last element in the options list (it was a positional argument that was the last argument):

auto it = std::find_if(parsed.options.begin(), parsed.options.end(),
    [](po::option const& o){ return o.string_key == "filename"; });
if (it == parsed.options.end()) {
    // fail: missing filename);
if (it->position_key != -1 && it != std::prev(parsed.options.end())) {
    // fail: filename was positional but wasn't last