How to add description for positional options boost :: program_options?

I would like to make a positional, list programmatic option with boost_program_options , which does not allow using named program parameters (for example, --files ).

I have the following code snippet:

 #include <boost/program_options.hpp> #include <iostream> #include <string> #include <vector> 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<std::vector<std::string>>()->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 << "Couldn't parse command line arguments properly:\n"; std::cerr << e.what() << '\n' << '\n'; std::cerr << desc << '\n'; return 1; } if(vm.count("help") || !vm.count("files")) { std::cout << desc << "\n"; return 1; } } 

The problem is that I can read the list of files as lists of positional arguments as follows:

 ./a.out file1 file2 file3 

but unfortunately it is also (which I would like to disable)

 ./a.out --files file1 file2 file3 

The problem is also in help, which gives:

 ./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 

This way, my desired scenario will be more like (os looks like):

 ./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 deleting the files parameters from desc.add_option()(...) it stops working, so I think I need it.

+5
source share
1 answer

Regarding the question asked in the heading, “How to add a description for boost :: program_options” positional parameters? ", there is no functionality for this in the library. You must handle this part yourself.

As for the body of the question ... it is possible, but a little around.

Positional parameters map each position to a name, and names must exist. From what I can say in the code ( cmdline.cpp ), the unregistered flag unregistered not be set for arguments that are positional. [ 1 ], [ 2 ]

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

  • Hide the --files from showing in the help. You will need to display the appropriate help for the positional parameters yourself, but this is no different from the previous one.
  • Add our own check between parsing and storing parsed options in variables_map .

Hiding --files with

Here we take advantage of 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); 

Therefore, we can put our files parameter in a hidden options_description and create a composite, which we will use only for the parsing stage. (see code below)

Preventing Explicit --files

We need to intercept a list of parameters between parsing and storing them in variables_map .

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

Source code example

See on Coliru

 #include <boost/program_options.hpp> #include <iostream> #include <string> #include <vector> namespace po = boost::program_options; int main(int argc, const char* argv[]) { std::vector<std::string> file_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 << "Couldn't parse command line arguments properly:\n"; std::cerr << e.what() << '\n' << '\n'; std::cerr << visible_options << '\n'; return 1; } if (vm.count("help") || !vm.count("files")) { std::cout << desc << "\n"; return 1; } if (!file_names.empty()) { std::cout << "Files: \n"; for (auto const& file_name : file_names) { std::cout << " * " << file_name << "\n"; } } } 

Test output

Valid parameters:

 >example abc --test de Files: * a * b * c * d * e 

Invalid parameters:

 >example abc --files de Couldn't parse command line arguments properly: unrecognised option 'files' Allowed options: --help produce help message --test test option 
+5
source

Source: https://habr.com/ru/post/1257894/


All Articles