The traditional way of indicating the end of options for command line programs is with the option --
. How can I get boost::program_options to recognize this as an option and accept the rest of the command line as positional arguments? The following doesn't work:
namespace po = boost::program_options;
po::positional_options_description posOpts;
posOpts.add("keywords", 1);
posOpts.add("input", 1);
std::vector<std::string> final_args;
po::options_description desc("Allowed Options");
desc.add_options()
...
("", po::value< std::vector<std::string> >(&final_args)->multitoken(), "end of options")
...
;
po::command_line_parser(argc, argv).options(desc).positional(posOpts).run();
If I give foo bar
as arguments, I get nothing in final_args
(as expected), but also when I give -- foo bar
as arguments (when I would expect to find final_args[0] == "foo"
and final_args[1] == "bar"
). I'm assuming here that --
is a long argument with the empty string as its argument name. If, instead, it's supposed to be interpreted as a short argument, with -
as the argument name, how do I specify that? Changing the argument specification from ""
to ",-"
doesn't affect the result, so far as I can see.
How does one get boost::program_options to handle --
correctly?
Edit: Here's an attempt to do what Tim Sylvester suggested by creating an extra_style_parser
:
std::vector<po::option> end_of_opts_parser(std::vector<std::string>& args) {
std::vector<po::option> result;
std::vector<std::string>::const_iterator i(args.begin());
if (i != args.end() && *i == "--") {
for (++i; i != args.end(); ++i) {
po::option opt;
opt.string_key = "pargs";
opt.original_tokens.push_back(*i);
result.push_back(opt);
}
args.clear();
}
return result;
}
"pargs"
was added to the options like this:
("pargs", po::value< std::vector<std::string> >(&pargs), "positional arguments")
Running this with a --
in the argument list causes a required_option
exception. (I get similar results if instead of making a po::option
for each trailing arg, I pack them all into po::option::original_tokens
in one po::option
.)
There's a simple, unsatisfying workaround: Before handing off argv
to command_line_parser
, check whether --
occurs in it. If so, reset argc
to the position of --
to hide it and the arguments trailing it from command_line_parser
. Then, when finished parsing, deal with the positional arguments after --
by hand. Blech!
I’m a bit confused because boost::program_options
already does this all by itself:
$ ./testprog --the-only-option=23 aa --unknown bb
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::program_options::unknown_option> >'
what(): unknown option unknown
Aborted
$ ./testprog --the-only-option=23 -- aa --unknown bb
the only option: 23
positional: aa
positional: --unknown
positional: bb
The program:
#include <boost/program_options.hpp>
#include <iostream>
#include <vector>
using namespace std;
namespace po = boost::program_options;
static bool handle_command_line(int argc, char *argv[])
{
po::options_description desc("Allowed options");
desc.add_options()
("help", "Describe command line options")
("the-only-option", po::value<string>(), "some option")
;
po::options_description hidden;
hidden.add_options()
("positional", po::value<vector<string> >())
;
po::options_description all_options;
all_options.add(desc).add(hidden);
po::positional_options_description p;
p.add("positional", -1);
po::variables_map vm;
po::store(po::command_line_parser(argc, argv).
options(all_options).positional(p).run(), vm);
po::notify(vm);
if (vm.count("help")) {
cout << "Usage: " << argv[0] << " [options] [args]" << endl;
cout << desc << "\n";
return false;
}
if (vm.count("the-only-option"))
cout << "the only option: " << vm["the-only-option"].as<string>() << endl;
if (vm.count("positional")) {
const vector<string> &v = vm["positional"].as<vector<string> >();
vector<string>::const_iterator it = v.begin();
for (; it != v.end(); ++it)
cout << "positional: " << *it << endl;
}
return true;
}
int main(int argc, char *argv[])
{
if (!handle_command_line(argc, argv))
return 1;
return 0;
}
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