What is the format of BOOST program options command lines? - c++

I have two switches, 'i' and 'p' that represent IPAddress and Port respectively.
What is the format of the command line?
I have tried:
app -i192.168.1.1 -p12345
app -i 192.168.1.1 -p 12345
app -i=192.168.1.1 -p=12345
app -i='192.168.1.1' -p='12345'
app --IPAddress 192.168.1.1 --Port12345
My application is having a problem with the IPAddress, and troubleshooting with DDD is unrevealing as I get for the vm.
Also, the app is running as a daemon, so my cout statements for the IP address and Port are going into oblivion, and printing to the syslog is hindered by the fact that outputting the values is not a const char*.
I plan to use program options for other things as well, but I am in over my head a bit with this.
po::options_description config("Configuration");
config.add_options()
("IPAddress,i","IP Address")
("Port,p","Port")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, config),
vm);
po::notify(vm);
//...and this is how the values are used
int retval = getaddrinfo((vm["IPAddress"].as< string >()).c_str(),(vm["Port"].as<string>()).c_str(), &hint, &list);
Here is a complete program...nothing is printed to the console after 'Values':
#include <sstream>
#include <algorithm>
#include <stdlib.h>
#include <iterator>
#include <string>
//Using boost program options to read command line and config file data
#include <boost/program_options.hpp>
using namespace std;
using namespace boost;
namespace po = boost::program_options;
int main (int argc, char *argv[])
{
po::options_description config("Configuration");
config.add_options()
("IPAddress,i","IP Address")
("Port,p","Port")
;
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, config),vm);
po::notify(vm);
cout << "Values\n";
cout << (vm["IPAddress"].as< string >()).c_str();
cout << " " << (vm["Port"].as<string>()).c_str();
return 0;
}
Are the inputted values somehow unprintable?
Here is gdb output, seems to be be cast problem:
28 string address = (vm["IPAddress"].as< string >()).c_str();
(gdb) n
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::bad_any_cast> >'
what(): boost::bad_any_cast: failed conversion using boost::any_cast
Program received signal SIGABRT, Aborted.
0x0000003afd835935 in raise () from /lib64/libc.so.6

BOOST Program options support the common command line flavours known from Unix systems.
Thus those two should work (they are working for me)
app -i 192.168.1.1 -p 12345
app --IPAddress=192.168.1.1 --Port=12345
Remarks:
The documentation with basic tutorial is at boost.org (probably you know this already)
writing a standalone unit test for this is certainly a good advice; boost also provides an easy-to-use test framework for C++

Related

Change the metavars in boost::program_options

This is a purely aesthetic issue.
I have written a CLI program in C++ and use boost::program_options to pasrse its args.
Per default, boost names all meta variables arg. I'd like to change that.
Here's the code:
#include <iostream>
using std::cerr;
using std::cout;
#include <optional>
using std::optional;
#include <string>
using std::string;
#include <boost/program_options.hpp>
using boost::program_options::unknown_option;
#include "Net.h"
#include "GameServer.h"
using proto::GameServer;
namespace args = boost::program_options;
static auto parseArgs(int argc, const char *argv[])
{
args::options_description desc("Command line options");
desc.add_options()
("help,h", "Show this page")
("address,a", args::value<string>()->default_value(net::Defaults::HOST), "IP address to listen on")
("port,p", args::value<unsigned short>()->default_value(net::Defaults::PORT), "Port to listen on")
;
optional<args::variables_map> result;
args::variables_map varMap;
try {
args::store(args::parse_command_line(argc, argv, desc), varMap);
} catch (unknown_option const &error) {
cerr << "Invalid option: " << error.get_option_name() << "\n";
cerr << desc << "\n";
return result;
}
args::notify(varMap);
if (varMap.count("help")) {
cout << desc << "\n";
return result;
}
return result = varMap;
}
int main(int argc, const char *argv[])
{
auto parsedArgs = parseArgs(argc, argv);
if (!parsedArgs.has_value())
return 1;
auto args = parsedArgs.value();
auto address = args.at("address").as<string>();
auto port = args.at("port").as<unsigned short>();
GameServer server(address, port);
server.listen();
}
And the output generated by the help page:
Command line options:
-h [ --help ] Show this page
-a [ --address ] arg (=127.0.0.1) IP address to listen on
-p [ --port ] arg (=9000) Port to listen on
I'd like to rename arg for -a to ip_address and for -p to portnum respectively.
#prehistoricpenguin's answer brought me onto the right track. In boost, the typed_value class has a method value_name() to set the argument name, which works analogous to setting the defaults, i.e. it modifies the value object and returns it for subsequent operations:
static auto parseArgs(int argc, const char *argv[])
{
options_description desc("Command line options");
desc.add_options()
("help,h", "Show this page")
("address,a", value<string>()->default_value(HOST)->value_name("ip_address"),
"IP address to connect to")
("port,p", value<unsigned short>()->default_value(PORT)->value_name("portnum"),
"Port to connect to");
return parseArgDesc(argc, argv, desc);
}
Resulting in:
Command line options:
-h [ --help ] Show this page
-a [ --address ] ip_address (=127.0.0.1)
IP address to connect to
-p [ --port ] portnum (=9000) Port to connect to
Seems it's an undocumented part of boost::program_options, I have a quick look at the implementation of boost::program_options and find there is a global variable which is used to control the behavior, so we come up with one line code hack:
args::arg = "i_am_not_arg";
Modify a global variable is not an elegant way, but I haven't found any usable APIs to do it, you may do more research and try to find a better solution(Don't forget to notify me here!)
Insert this line in some place of your code, then we get the output for --help command line:
->./a.out --help
Command line options:
-h [ --help ] Show this page
-a [ --address ] i_am_not_arg (=1234) IP address to listen on
-p [ --port ] i_am_not_arg (=42) Port to listen on
Suggestions for asking question on SO:
provide a minimal, reproducible program. Your code doesn't compile on my machine, because you used variables from files that are not provided.
Online demo

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

Why boost::program_options::bool_switch is not behaving like I expect?

Code below uses po::bool_switch(&flag) in hope of automatically assigning the correct value to flag.
My compile command is clang++ -std=c++11 test.cpp -o test -lboost_program_options
So I run the program with ./test -h which shows no help message.
Why so?
#include <iostream>
#include <boost/program_options.hpp>
namespace
{
namespace po = boost::program_options;
}
int main(int ac, char ** av)
{
bool flag;
po::options_description help("Help options");
help.add_options()
( "help,h"
, po::bool_switch(&flag)->default_value(false)
, "produce this help message" );
po::variables_map vm;
po::parsed_options parsed
= po::parse_command_line(ac,av,help);
po::store(po::parse_command_line(ac,av,help),vm);
if (flag)
{
std::cout << help;
}
po::notify(vm);
return 0;
}
You should call notify after parsing and storing arguments. store just fills in internal data structures of variables_map. notify publishes them.
Your example is nearly exactly like the one in "Getting started section" in tutorial.
And here they give a pretty bold warning not to forget it:
Finally the call to the notify function runs the user-specified notify functions and stores the values into regular variables, if needed.
Warning:
Don't forget to call the notify function after you've stored all parsed values.
This should work:
po::variables_map vm;
po::parsed_options parsed
= po::parse_command_line(ac,av,help);
po::store(po::parse_command_line(ac,av,help),vm);
po::notify(vm); // call after last store, and before accesing parsed variables
if (flag)
{
std::cout << help;
}

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.