How to add a description to boost::program_options' positional options?

I would like to make a positional, list program option with boost_program_options that do not allow named program options (like --files ). I have the following snippet of code:

#include #include #include #include namespace po = boost::program_options; int main(int argc, const char* argv[]) < po::options_description desc("Allowed options"); desc.add_options()("help", "produce help message") ( "files", po::value>()->required(), "list of files"); po::positional_options_description pos; pos.add("files", -1); po::variables_map vm; try < po::store(po::command_line_parser(argc, argv).options(desc).positional(pos).run(), vm); po::notify(vm); >catch(const po::error& e) < std::cerr if(vm.count("help") || !vm.count("files")) < std::cout > 
The problem is that I can read files list as positional arguments lists as follows:
./a.out file1 file2 file3 
but unfortunately like this as well ( which I would like to disable )
./a.out --files file1 file2 file3 
The problem is also with the help which yields:
./a.out Couldn't parse command line arguments properly: the option '--files' is required but missing Allowed options: --help produce help message --files arg list of files 
So my desired scenario would be more like (os similar):
./a.out Couldn't parse command line arguments properly: [FILES . ] is required but missing Allowed options: --help produce help message --optionx some random option used in future [FILE . ] list of files 

After I remove files options from desc.add_option()(. ) it stop working so I believe I need it there.

asked Oct 8, 2016 at 12:01 23.8k 47 47 gold badges 138 138 silver badges 254 254 bronze badges

Why do you need to remove the ability to specify the input files with the named parameter? It's not detrimental to anything, so why go out of your to disable it?

Commented Oct 8, 2016 at 13:46

@DanMašek I believe it's not that clear to the user since this allows both ways to be valid input strategies (where I specifically want only one and have help to support it)

Commented Oct 8, 2016 at 13:51

OK. There you have what seems to me the least invasive solution. It still allows the positional options to be scattered all throughout the argument list, but you could easily add more validation to restrict it more.

Commented Oct 8, 2016 at 16:24

BTW, I think you should change the title something that better reflects the body of your question. It's a tough one, but something like "Using positional options with their explicit variant forbidden" or something along those lines might be better.

Commented Oct 8, 2016 at 16:36

1 Answer 1

As to the question posed in the title, "How to add a description to boost::program_options' positional options?", there's no functionality provided for this in the library. You need to handle that part yourself.

As for the body of the question. it's possible, but in a slightly round-about way.

The positional options map each position to a name, and the names need to exist. From what I can tell in the code ( cmdline.cpp ), the unregistered flag won't be set for arguments that are positional. [1], [2]

So, to do what you want, we can do the following:

Hiding --files from help

Here we take advantage of the the fact that we can create composite options_description using the add(. ) member function:

po::options_description desc_1; // . po::options_description desc_2; // . po::options_description desc_composite; desc_composite.add(desc_1).add(desc_2); 

We can therefore place our files option into a hidden options_description , and create a composite that we will use only for the parsing stage. (see code below)

Preventing explicit --files

We need to intercept the list of options between parsing and storing them into the variables_map .

The run() method of command_line_parser returns an instance of basic_parsed_options , whose member options holds a vector of basic_option s. There is an element for each parsed argument, and any positional options are enumerated starting from 0 , any non-positional options have position -1 . We can use this to perform our own validation and raise an error when we see --files as an explicit (non-positional) argument.

Example Source Code

#include #include #include #include namespace po = boost::program_options; int main(int argc, const char* argv[]) < std::vectorfile_names; po::options_description desc("Allowed options"); desc.add_options() ("help", "produce help message") ("test", "test option"); std::string const FILES_KEY("files"); // Hide the `files` options in a separate description po::options_description desc_hidden("Hidden options"); desc_hidden.add_options() (FILES_KEY.c_str(), po::value(&file_names)->required(), "list of files"); // This description is used for parsing and validation po::options_description cmdline_options; cmdline_options.add(desc).add(desc_hidden); // And this one to display help po::options_description visible_options; visible_options.add(desc); po::positional_options_description pos; pos.add(FILES_KEY.c_str(), -1); po::variables_map vm; try < // Only parse the options, so we can catch the explicit `--files` auto parsed = po::command_line_parser(argc, argv) .options(cmdline_options) .positional(pos) .run(); // Make sure there were no non-positional `files` options for (auto const& opt : parsed.options) < if ((opt.position_key == -1) && (opt.string_key == FILES_KEY)) < throw po::unknown_option(FILES_KEY); >> po::store(parsed, vm); po::notify(vm); > catch(const po::error& e) < std::cerr if (vm.count("help") || !vm.count("files")) < std::cout if (!file_names.empty()) < std::cout > > 

Test Output

>example a b c --test d e Files: * a * b * c * d * e 
>example a b c --files d e Couldn't parse command line arguments properly: unrecognised option 'files' Allowed options: --help produce help message --test test option