BOOST program_options: parsing multiple argument list - c++

I would like to pass the multiple arguments with positive or negative values.
Is it possible to parse it?
Currently I have a following initialization:
vector<int> IDlist;
namespace po = boost::program_options;
po::options_description commands("Allowed options");
commands.add_options()
("IDlist",po::value< vector<int> >(&IDlist)->multitoken(), "Which IDs to trace: ex. --IDlist=0 1 200 -2")
("help","print help")
;
and I would like to call:
./test_ids.x --IDlist=0 1 200 -2
unknown option -2
So,the program_options assumes that I am passing -2 as an another option.
Can I configure the program_options in such a way that it can accept the negative integer values?
Thanks
Arman.
EDIT:
BTW I was parsing it by the simple parser
store(command_line_parser(argc, argv).options(commands).run(), vm);
, but solution was to use the extended one:
parse_command_line

Have you tried "-2"?
Edit: Quoting doesn't seem to do the trick, however, changing the command line style works:
char* v[] = {"name","--IDlist=0","1","200","-2"};
int c = 5;
std::vector<int> IDlist;
namespace po = boost::program_options;
po::options_description commands("Allowed options");
commands.add_options()
("IDlist",po::value< std::vector<int> >(&IDlist)->multitoken(), "Which IDs to trace: ex. --IDlist=0 1 200 -2")
("help","print help")
;
po::variables_map vm;
po::store(parse_command_line(c, v, commands, po::command_line_style::unix_style ^ po::command_line_style::allow_short), vm);
po::notify(vm);
BOOST_FOREACH(int id, IDlist)
std::cout << id << std::endl;

NOTE: this is a remark to the accepted solution.
Disabling short options is the key. The solution above proposed by kloffy works great, but if you happen to use positional_option_description (e.g. to parse parameters without using an option like ls file.txt instead of ls --file=file.txt) you might have a hard time converting your code to do that using parse_command_line.
However you can also disable short options and keep using the basic_command_line_parser like this:
Replace
store(command_line_parser(argc, argv).options(commands).run(), vm);
with
store(command_line_parser(argc, argv).options(commands).style(
po::command_line_style::unix_style ^ po::command_line_style::allow_short
).run(), vm);

maybe try --IDlist "0, 1, 200, -2" or --IDlist="0, 1, 200, -2"

Related

boost program options count number of occurrences of a flag

I am trying to program in a way for the user of my program to specify the level of verbosity of my program from 0 to 3. I was told by someone that there might be a way to set up the program options so that I am able to detect the number of occurrences of a flag, and then run my program accordingly.
Example:
[none] -> level 0
-v -> level 1
-vv -> level 2
-vvv -> level 3
Does anyone know if this is possible? Do I just need to set up three different options, one for each possibility? I have tried to search around for a similar example however I fear I may be searching the wrong things.
I can't think of a nice way. boost::program_options option syntax is a little more structured (and arguably more simplistic) than getopt.
Here's one way:
#include <boost/program_options.hpp>
#include <iostream>
#include <algorithm>
#include <cstdlib>
int main(int argc, char**argv)
{
namespace po = boost::program_options;
std::string verbosity_values;
po::options_description desc("Command Line Options");
desc.add_options()("verbosity,v",
po::value(&verbosity_values)->implicit_value(""),
"verbose");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("verbosity")) {
verbosity_values += "v";
}
if (std::any_of(begin(verbosity_values), end(verbosity_values), [](auto&c) { return c != 'v'; })) {
std::cerr << "invalid verbosity" << std::endl;
std::exit(100);
}
std::cout << "verbosity: " << verbosity_values.size() << std::endl;
}
How it works:
define an option called "--verbosity" with a synonym "-v".
'verbosity' takes a string argument, which we default to ""
we check that the string contains only 'v's
if the variables_map contains a 'verbosity' argument, then -v or --verbosity must have been mentioned on the command line. Therefore, add a 'v' to the string.
verbosity is the length of the string.
example:
$ ./a.out -vvvv
verbosity: 4
$

Is this kind of repeatable options possible with boost::program_options?

I have options --foo (short form -f) and --bar that need special treatment, they are repeatable and order should matter. So, for the following:
program --foo 1 --z -f 2 --bar 3 --x --foo 4
I'd like to range a key value map being able to construct [("foo", 1), ("foo", 2), ("bar", 3), ("foo", 4)].
Please notice the order of this array of tuples, it's the same as that in the command line. I've discarded non-important options in the array, but they may be present in the command line nonetheless.
It seems the sole way to allow repeatable options with boost::program_options is calling composing() for any given option, but then, since each will store all their values in a vector, I lose the order I need for interlacing options.
So, can boost::program_options help with this?
EDIT
I've asked for alternative software recommendations here: https://softwarerecs.stackexchange.com/questions/31766/
And answered it using Poco.
Assuming you can accept having to put an equals sign between the --foo and the value, this might do what you want:
#include <iostream>
#include <boost/program_options.hpp>
#include <vector>
#include <iterator>
#include <algorithm>
int main(int argc, const char**argv)
{
std::vector<int> foos;
boost::program_options::options_description desc("Command Line Options");
desc.add_options()
("foo,F", boost::program_options::value(&foos)->multitoken(), "integers for foo");
std::cout << desc << std::endl;
boost::program_options::variables_map vm;
boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
boost::program_options::notify(vm);
std::copy(foos.begin(), foos.end(), std::ostream_iterator<int>(std::cout, ", "));
std::cout << std::endl;
return 0;
}
called with command line:
./a.out --foo=1 --foo=2
yields:
Command Line Options:
-F [ --foo ] arg integers for foo
1, 2,

boost program options - how to conditionally type positional args?

I'm using boost program options, and I want to interpret some positional arguments as either strings, or ints, based on a user specified command line switch. For example:
foo -asint outputfile 10 11 12
foo -asstr outputfile 10 11 12
would list (10,11,12) as ints in the first example and strings in the second.
I can't figure out how to do this using boost po. Here is a snippet of my command line parsing:
// basic options group
po::options_description genericOpts("allowed options");
genericOpts.add_options()
("help,h", "display help message / usage")
("asint,i", "interpret arguments ints instead of strings")
;
// hidden options group - don't show in help
po::options_description hiddenOpts("hidden options");
hiddenOpts.add_options()
("filename", po::value<string>()->required(),"output file")
("inputs", po::value<vector<string>>(), "inputs, either strings or ints")
;
po::options_description cmdline_options;
cmdline_options.add(genericOpts).add(hiddenOpts);
po::positional_options_description p;
p.add("filename",1).add("inputs", -1);
po::variables_map vm;
po::store(po::command_line_parser(argc, argv).
options(cmdline_options).positional(p).run(), vm);
Always read them as strings and do some post-processing depending on the other options.

boost::program_options - does it do an exact string matching for command line options?

There seems to be a problem the way boost::program_options's options_description matching is done.
int main(int argc, char* argv[])
{
boost::program_options::options_description desc("CmdLine utility");
desc.add_options()
("hel", "hel message")
("help", "produce help message")
("helps","helps message")
;
boost::program_options::variables_map vm;
boost::program_options::store(boost::program_options::parse_command_line(argc, argv,desc), vm);
boost::program_options::notify(vm);
if(vm.count("help")) {
std::cout << desc << std::endl;
}
if(vm.count("helps")) {
std::cout << "helps..." << std::endl;
}
if(vm.count("hel")) {
std::cout << "hel..." << std::endl;
}
return 0;
}
Output -
C:\code>cmd.exe --helps
helps...
C:\code>cmd.exe --help
helps...
C:\code>cmd.exe --hel
helps...
The output changes if I change the order in which options are added using add_options() call. Also it looks like program_options does not do a complete command string matching, so even if you enter a substring of the option, it will consider it as a valid option without doing a complete string comparison. If this is a boost::program_options feature, is there any way to force it to do exact string matching rather than doing it with substring matching ? (I am using Boost version 1.42)
By default, program_option has allow_guessing style bit on, so a substring match is sufficient. The behaviour you observe, where an option is matching a prefix of the command line, even when there's a different option that matches fully, is a bug. It's fixed in 1.45.
Maybe you called wrongly. You example is fine. Look at the output I got :
[vladimir#asa example]$ ./a.out --help
CmdLine utility:
--hel hel message
--help produce help message
--helps helps message
[vladimir#asa example]$ ./a.out --hel
hel...
[vladimir#asa example]$ ./a.out --helps
helps...

How to accept empty value in boost::program_options

I'm using boost::program_options library to process command line params.
I need to accept a file name via -r option, in case if it is empty (-r given without params) I need to use stdin.
desc.add_options()
("replay,r", boost::program_options::value<std::string>(), "bla bla bla")
In this case boost wouldn't accept -r without params and throw an exception.
default_value () option does not work as well as it would make library return value even if user didn't give -r option.
Any ideas how to work around?
Please use the implicit_value method, e.g
desc.add_options()
("replay,r", po::value<std::string>()->implicit_value("stdin"), "bla bla bla")
This makes the option accept either 0 or 1 token, and if no tokens are provided, it will act as if 'stdin' was provided. Of course, you can pick any other implicit value -- including empty string and '-' as suggested by mch.
You could try a trick with the multitoken and zero_tokens options:
using namespace std;
namespace po = boost::program_options;
vector<string> replay;
po::options_description desc("Allowed options");
desc.add_options()
("replay,r", po::value< vector<string> >(&replay)->multitoken()->zero_tokens(), "bla bla bla");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("replay"))
{
size_t s = vm["replay"].as< vector<string> >().size();
if (s == 0)
cout << "replay without args" << endl;
else if (s == 1)
cout << "replay with one arg" << endl;
else
cout << "replay with multiple args" << endl;
}
else
cout << "replay not specified" << endl;
Then just count the number of elements in the replay vector. You'll want to throw an error if multiple arguments are passed to the replay option.
I don't think any command line parsing libraries allow you to have options that can either take an argument or not. If an option requires an argument, you must give one. In this case, the standard practice (in *NIX anyway) is to use '-' as a filename to denote that you want to read from standard input.