nomad nomad - 1 year ago 260
C++ Question

default and implicit arguments in boost program options

I am seeing some strange behavior with boost program_options. I have an option that has a default and implicit value. To make a widely-compatible binary of my program, I compile it under Holy Build Box, which seems to work well. However, the problem I'm encountering is that when I provided this pre-compiled executable with an explicit argument for this option (i.e.

--writeMappings ./somefile.txt
) the result is parsed incorrectly. Rather than mapping the argument
to the option
, boost::program_options thinks that the argument to the
option is empty, and there is another option, the empty string
that has an argument of

I store the variable map in a JSON file, and the example here demonstrates exactly the behavior I'm seeing. Does anyone have an idea why this might occur? I assume that it's not the correct behavior. Strangely, when I compile the same code outside of Holy Build Box (using g++-4.9.1 instead of g++-4.8.2), the program works as expected, and the implicit argument is overridden by any explicit argument that is passed.

Here is a minimal example that reproduces the problem:

#include <iostream>
#include <vector>
#include <boost/program_options.hpp>

int main(int argc, char* argv[]) {
using std::cerr;
using std::string;
namespace po = boost::program_options;
string fname;
po::options_description generic("\n"
"basic options");
generic.add_options()("version,v", "print version string")
"writeMappings", po::value<string>(&fname)->default_value("")->implicit_value("-"),
"If this option is provided, then the quasi-mapping results will be written out in SAM-compatible "
"format. By default, output will be directed to stdout, but an alternative file name can be "
"provided instead.");
po::options_description visible("options");

po::variables_map vm;
try {
auto orderedOptions =
po::command_line_parser(argc, argv).options(visible).run();

po::store(orderedOptions, vm);
std::cerr << "writeMappings = " << fname << '\n';
} catch (po::error& e) {
std::cerr << "Exception : [" << e.what() << "]. Exiting.\n";
return 0;

I compiled with the following:

g++ -std=c++11 -o opttest main.cpp -L /home/boost_1_61_0/lib -I /home/boost_1_61_0/include -lboost_program_options

and I get the following output with the corresponding invocations:

$ ./opttest
writeMappings =
$ ./opttest --writeMappings
writeMappings = -
$ ./opttest --writeMappings stuff
writeMappings = -

The final invocation is the problematic one. Given the explicit option, I'd expect
writeMappings = stuff
. I will note, however, that when I use alternative syntax, it gets parsed correctly:

$ ./opttest --writeMappings=stuff
writeMappings = stuff

However, I'd really like this to work with the more common
--option argument

Answer Source

From the docs, emphasis mine:

typed_value * implicit_value(const T & v);

Specifies an implicit value, which will be used if the option is given, but without an adjacent value. Using this implies that an explicit value is optional, but if given, must be strictly adjacent to the option, i.e.: '-ovalue' or '--option=value'. Giving '-o' or '--option' will cause the implicit value to be applied.

So when you write:

$ ./opttest --writeMappings stuff

stuff isn't strictly adjacent to the option writeMappings, so is interpreted as a separate option entirely. You will have to write:

$ ./opttest --writeMappings=stuff

to get the value of "stuff" instead of "-".