I have a positional option (a file name), and I want it to be the very last option. 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 also to have the ability to 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 any way 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:
store(
command_line_parser(argc, argv).options(opts_desc).positional(pos_opts_desc).run(),
opts_var_map);
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.
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::store(
po::command_line_parser(argc, argv).options(opts_desc).positional(pos_opts_desc).run(),
opts_var_map);
But we can hold onto it and examine its contents:
auto parsed = po::command_line_parser(argc, argv)
.options(opts_desc)
.positional(pos_opts_desc)
.run();
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
}
variables_map
is as the name suggests a std::map
, which allows us to use regular STL functions on it.
if ( vm.count("filename") ) {
if ( vm.find("filename") != std::prev(vm.rbegin()).base() ) {
std::cout << "filename must go at the end.";
}
}
Test cases:
g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp -lboost_system -lboost_program_options \
&& echo -n "Case 1 " && ./a.out asdf --foo=12 && echo \
&& echo -n "Case 2 " && ./a.out --foo=12 asdf && echo \
&& echo -n "Case 3 " && ./a.out asdf && echo \
&& echo -n "Case 4 " && ./a.out --foo=12 && echo \
&& echo -n "Case 5 " && ./a.out && echo \
&& echo -n "Case 6 " && ./a.out --foo=12 asdf asdf
Result:
Case 1 filename must go at the end.
Case 2
Case 3
Case 4
Case 5
Case 6 option '--filename' cannot be specified more than once
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With