How to force boost program_options to check options strictly? - c++

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?

Related

How to properly use boost::program_options::implicit_value for vector of string?

Proof of code:
boost::program_options::options_description options;
Parser::Parser(): options("Allowed options")
{
options.add_options()
("help,h", "produce help message")
("type,t", po::value<std::string>()->required()->implicit_value(""), "Type")
}
This line is ok:
("type,t", po::value<std::string>()->required()->implicit_value(""), "Type")
How can I add this line to work correctly?:
("file,f", po::value< std::vector<std::string> >()->required()->multitoken()->implicit_value(std::vector<std::string>(0,"")), "File(s)")
Here is vector of string-s.
You just need to help the options-description to know how to present the default value to the end user.
That is, usually implicit_value would use lexical_cast<> to get the textual representation, but that (obviously) doesn't work for vector<string>. So, supply your own textual representation:
("file,f", po::value<strings>()->required()
->implicit_value(strings { "santa", "claus" }, "santa,claus"), "File(s)");
Full Demo
Live On Coliru
#include <iostream>
#include <boost/program_options.hpp>
namespace po = boost::program_options;
int main(int argc, char** argv) {
po::options_description options/*("Allowed options")*/;
using strings = std::vector<std::string>;
options.add_options()
("help,h", "produce help message")
("file,f", po::value<strings>()->required()->implicit_value(strings { "santa", "claus" }, "santa,claus"), "File(s)");
std::cout << options << "\n";
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, options, po::command_line_style::default_style), vm);
po::notify(vm);
auto types = vm["file"].as<strings>();
for (auto t : types)
std::cout << "Got: " << t << "\n";
}
Prints:
-h [ --help ] produce help message
-f [ --file ] [=arg(=santa,claus)] File(s)
Got: santa
Got: claus

boost program_option case insensitive parsing

Has anyone worked out how to get boost program options to parse case insensitive argument lists
In the boost documentation, it appears that it is supported. See http://www.boost.org/doc/libs/1_53_0/boost/program_options/cmdline.hpp
Namely, setting the style_t enum flag such as long_case_insensitive. However, I'm not sure how to do it. Eg how would you get the following code snippet to accept --Help or --help or --HELP
po::options_description desc("Allowed options");
desc.add_options()
("help", "produce help message")
("compression", po::value<double>(), "set compression level")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);
if (vm.count("help")) {
cout << desc << "\n";
return 0;
}
You can modify the style when you call store. I believe this should work for you:
namespace po_style = boost::program_options::command_line_style;
po::variables_map vm;
po::store(po::command_line_parser(argc, argv).options(desc)
.style(po_style::unix_style|po_style::case_insensitive).run(), vm);
po::notify(vm);

Why is boost::program_options accepting chopped words?

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);

How do I detect typo with Boost.program_options?

I use boost.program_options library. Consider this simplified case.
po::options_description desc("Usage");
desc.add_options()
("uninstall,u", "uninstall program")
("custom,c", po::wvalue<std::wstring>(), "specify custom action");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
I want to produce error on such command-line:
testprog.exe -u c- action1
Note, user made a typo "c-" instead of "-c". But the parser understands this as a single -u option. How do I handle such cases?
I want to produce error on such command-line:
testprog.exe -u c- action1
Note, user made a typo "c-" instead of "-c". But the parser
understands this as a single -u option. How do I handle such cases?
Instruct the program_options library to accept no positional arguments and you get the desired behavior
code & compile:
macmini:stackoverflow samm$ cat po.cc
#include <boost/program_options.hpp>
#include <boost/version.hpp>
#include <iostream>
int
main(int argc, char* argv[])
{
namespace po = boost::program_options;
po::options_description desc("Usage");
desc.add_options()
("uninstall,u", "uninstall program")
("custom,c", po::wvalue<std::wstring>(), "specify custom action")
;
po::variables_map vm;
po::command_line_parser cmd_line( argc, argv );
cmd_line.options( desc );
cmd_line.positional( po::positional_options_description() );
try {
po::store( cmd_line.run(), vm );
po::notify(vm);
} catch ( const std::exception& e ) {
std::cerr << e.what() << std::endl;
return -1;
}
return 0;
}
macmini:stackoverflow samm$ g++ po.cc -I /opt/local/include -L/opt/local/lib -lboost_program_options -Wl,-rpath,/opt/local/lib
run:
macmini:stackoverflow samm$ ./a.out -u c- action1
too many positional options
macmini:stackoverflow samm$ ./a.out -u -c action1
macmini:stackoverflow samm$
Compare argc-1 to the number of arguments found by program_options? If it doesn't match, there is a syntax error.
It won't catch all cases but it may catch those important to you.
I think the only way you can do this is to ensure that each argument you require is present, for example by testing the count of each type.
if (vm.count("uninstall")) { ... }
if (vm.count("custom")) { ... }
You can generate an error if the options you require are not present (i.e. count is 0) or are present (for example -u and -c cannot be specified would be count of both is >0).

"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;
}