Why is boost::program_options accepting chopped words? - c++

I have the following program:
#include <boost/program_options.hpp>
bool check_options(int argc, char** argv)
{
using namespace boost::program_options;
variables_map vm;
// Command line options
std::string cfg_file_name;
options_description cmd_line("Allowed options");
cmd_line.add_options()
("help", "produce this help message")
;
store(parse_command_line(argc, argv, cmd_line), vm);
notify(vm);
if(vm.count("help"))
{
std::cout << cmd_line << std::endl;
return false;
}
return true;
}
int main(int argc, char** argv)
{
if(!check_options(argc, argv))
return 1;
return 0;
}
When I run it with ./myprg --help I get the result I expect:
Allowed options:
--help produce this help message
However I get the same result even if I run: ./myprg --h or ./myprg --he or ./myprg --hel. Shouldn't those last options throw an error?

Seems like accepting partial matches is the default_style for boost::option.
According to an answer at the Boost site http://lists.boost.org/boost-users/2007/02/25861.php
this default can be changed to require a full match, by passing an extra parameter to parse_command_line.
EDIT by OP:
Actually instead of parse_command_line I had to use the more general command_line_parser (which allows style changings), thus replacing the store(... line with this one:
store(command_line_parser(argc, argv).options(cmd_line).style(command_line_style::default_style & ~command_line_style::allow_guessing).run(), vm);

Related

How to force boost program_options to check options strictly?

This is my minimal example, basically copied from boost web site:
#include <boost/program_options.hpp>
#include <iostream>
namespace po = boost::program_options;
int main(int argc, char **argv) {
po::options_description desc("Test options");
desc.add_options()
("help", "Print help")
;
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("help")) {
std::cout << desc << std::endl;
}
}
When I run:
./main --help
it prints help, as expected. When I run:
./main --foo
it throws unknown_option error, as expected. But when I run:
./main foobar
it ignores the argument and no error is raised. I specify no positional arguments, so I expect boost to raise error here as well.
Why does boost behave this way (I expect that there is some logical reason that I don't see yet)?
What can I do to force boost to strictly check the options provided and raise error on anything else?

Sane way to use boost::program_options with INI and command line?

I want to write a program that parses a config file, and allows the command line to override what's written there. So I can have a config file that says:
[section1]
opt1=42
[section2]
opt2=17
And then, I can run the command with:
./so --opt2=3
And the program will get opt1 as 42 and opt2 and 3. I use the following program to try and do it:
#include <fstream>
#include <boost/program_options.hpp>
namespace po = boost::program_options;
int main(int argc, char *argv[]) {
po::options_description options1("section1");
options1.add_options()
("opt1", po::value<int>(), "Option 1");
po::options_description options2("section2");
options2.add_options()
("opt2", po::value<int>(), "Option 2");
po::options_description options;
options.add(options1);
options.add(options2);
po::variables_map values;
po::store( po::command_line_parser( argc, argv ).options(options).run(), values );
std::ifstream iniFile( "options.ini" );
po::store(
parse_config_file( iniFile, options ),
values );
}
This, of course, doesn't work. Boost::program_options wants opt1 under section1 to be called section1.opt1. If I do that, however, my program becomes harder to maintain on two fronts:
I need to define two options_descriptions, one for the INI and one for the command line.
Since the options' keys are now different, I need to manually merge the two.
Is there a way achieve this without doing the work manually?
The trivial solution is, to not use the sections. There might be some confusion around "sections" in the options descriptions vs. sections in ini-files.
The sections in ini-files refer only to options named with embedded periods: "section1.opt1". So you can simply write the config file as:
opt1=42
# perhaps with a comment
opt2=17
See it Live On Coliru
#include <boost/program_options.hpp>
#include <fstream>
#include <iostream>
namespace po = boost::program_options;
int main(int argc, char* argv[]) {
po::options_description options;
options.add_options()
("opt1", po::value<int>(), "Option 1")
("section2.opt2", po::value<int>(), "Option 2");
std::cout << options << "\n";
po::variables_map values;
po::store(po::parse_command_line(argc, argv, options), values);
std::ifstream iniFile("options.ini");
po::store(parse_config_file(iniFile, options), values);
auto report = [&values](char const* name) {
if (auto opt = values[name]; !opt.empty())
std::cout << name << ": " << opt.as<int>() << "\n";
};
report("opt1");
report("opt2");
report("section1.opt1");
report("section2.opt2");
}
Prints
echo "opt1=42" >> options.ini; ./a.out --section2.opt2 99
--opt1 arg Option 1
--section2.opt2 arg Option 2
opt1: 42
section2.opt2: 99

Does boost::program_options support requiring one of a series of alternatives?

I'm using the boost::program_options to specify the arguments to my C++ application. Is there a way to specify that one argument is required from a set of alternatives?
<application> [--one int-value1 | --two string-value2 | --three]
In the above, the user must pass exactly one of the alternatives: --one, --two, or --three.
I could do this manually, but hope there is a built-in mechanism instead of this:
#include <boost/program_options.hpp>
namespace po = boost::program_options;
int main(int argc, char *argv[]) {
po::options_description options;
int band;
std::string titles_path;
options.add_options()
("one", po::value<int>(&band)->default_value(1))
("two", po::value<std::string>(&titles_path))
("three");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, options), vm);
if (1 != (vm.count("one") + vm.count("two") + vm.count("three"))) {
std::cerr << options << std::endl;
return -11;
}
return 0;
}
Is there a better way to do this with boost options?
The program_options validator does not support parameter inter-dependencies (including negative ones).
Probably what you do right now is in fact the best option.

boost::program_options - parsing multiple command line arguments where some are strings including spaces and characters

I want to parse multiple command line arguments using boost::program_options. However, some arguments are strings enclosed in double quotes. This is what I have -
void processCommands(int argc, char *argv[]) {
std::vector<std::string> createOptions;
boost::program_options::options_description desc("Allowed options");
desc.add_options()
("create", boost::program_options::value<std::vector<std::string> >(&createOptions)->multitoken(), "create command")
;
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("create") >= 1) {
std::string val1 = createOptions[0];
std::string val2 = createOptions[1];
...
// call some function passing val1, val2.
}
}
this works fine when I do
cmdparsing.exe --create arg1 arg2
But does not work when I do
cmdparsing.exe --create "this is arg1" "this is arg2"
from windows command line. For second option, it gets converted to ["this" "is" "arg1" "this" "is" "arg2"] in createOptions vector. Thus, val1 gets "this" and val2 gets
"is" instead of "this is arg1" and "this is arg2" respectively.
How can I use boost::program_option to make this work ?
I fixed it using a native Windows function which handles command line arguments differently. See CommandLineToArgvW for details. Before passing it to processCommands(), I am modifying my argv[] and argc using the method mentioned above. Thank you Bart van Ingen Schenau for your comment.
#ifdef _WIN32
argv = CommandLineToArgvW(GetCommandLineW(), &argc);
if (NULL == argv)
{
std::wcout << L"CommandLineToArgvw failed" << std::endl;
return -1;
}
#endif
You should be able to achieve this with positional options:
positional_options_description pos_desc;
pos_desc.add("create", 10); // Force a max of 10.
Then when you parse the command line add this pos_desc:
using namespace boost::program_options;
command_line_parser parser{argc, argv};
parser.options(desc).positional(pos_desc);
store(parser.run(), vm);

"Multiple occurrences" exception for boost program_options

I am writing the following code on boost's program_options (version 1.42). This seems straight-forward and taken pretty much as is from the tutorial. However, I get a "multiple_occurrences" error. Further investigation discovers that it's (probably) the "filename" parameter that raises it.
The parameters I am giving are:
3 1 test.txt 100
I have no insight to it whatsoever.. any help will be appreciated.
po::options_description common("Common options");
common.add_options()
("help", "produce help message")
("motif_size", po::value<int>(&motif_size), "Size of motif (subgraph)")
("prob", po::value<double>(&prob), "Probably to continue examining an edge")
("filename", po::value<string>(&input_filename), "Filename of the input graph")
("repeats", po::value<int>(&n_estimates), "Number of estimates")
;
po::options_description all;
all.add(common);
po::positional_options_description p;
p.add("motif_size", 0).add("prob", 1).add("filename", 2).add("repeats", 3);
po::variables_map vm;
po::store(po::command_line_parser(argc, argv).
options(all).positional(p).run(), vm);
po::notify(vm);
EDIT:
the second parameter to po::positional_options_description::add is the max count, not the position. The position is implied in the order you specify the positional options. So
p.add("motif_size", 0).add("prob", 1).add("filename", 2).add("repeats", 3);
should be
p.add("motif_size", 1).add("prob", 1).add("filename", 1).add("repeats", 1);
Here's a compilable snippet
include <boost/program_options.hpp>
#include <iostream>
#include <string>
int
main(unsigned argc, char** argv)
{
namespace po = boost::program_options;
po::options_description common("Common options");
common.add_options()
("help", "produce help message")
("motif_size", po::value<int>(), "Size of motif (subgraph)")
("prob", po::value<double>(), "Probably to continue examining an edge")
("filename", po::value<std::string>(), "Filename of the input graph")
("repeats", po::value<int>(), "Number of estimates")
;
po::options_description all;
all.add(common);
po::positional_options_description p;
p.add("motif_size", 1).add("prob", 1).add("filename", 1).add("repeats", 1);
po::variables_map vm;
try {
po::store(po::command_line_parser(argc, argv).
options(all).positional(p).run(), vm);
po::notify(vm);
} catch ( const boost::program_options::error& e ) {
std::cerr << e.what() << std::endl;
}
return 0;
}
and sample invocation.
macmini:~ samm$ g++ parse.cc -lboost_program_options
macmini:~ samm$ ./a.out 3 1 test.txt 100
macmini:~ samm$
My original answer is below.
What version of program_options? I had the same problem using boost 1.39, to solve it I ended up using boost 1.42.
Here's a link to the ticket describing the problem, and a patch to apply if you don't want to or can't upgrade your copy of boost. To use the new functionality, do something like this
try {
// argument parsing goes here
} catch ( const boost::program_options::multiple_occurrences& e ) {
std::cerr << e.what() << " from option: " << e.get_option_name() << std::endl;
}