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