//Using boost program options to read command line and config file data
#include
using namespace std;
using namespace b
You need to declare the ip-address and port as strings when you add the options:
config.add_options()
("IPAddress,i", po::value<std::string>(), "IP Address")
("Port,p", po::value<std::string>(), "Port")
;
This same message can also occur if you are not handling optional arguments correctly.
Sam's solution nails required arguments and the OP's code suggests required - just mark them required. For optional inputs, the Boost PO tutorial gives us a template for checking if the option exists before converting it:
if(vm.count("address"))
{
const std::string address = vm["IPAddress"].as<std::string>();
std::cout << "address: " << address << std::endl;
}
if(vm.count("port"))
const std::string port = vm["Port"].as<std::string>();
std::cout << "port: " << port << std::endl;
}
My problem - I had copied/pasted and forgotten to align the if test with the usage!
You see the boost::bad_any_cast
exception thrown from the po::variables_map
because the two const char*
argument overload of po::options_description_easy_init::operator() does not specify a po::value_semantic
type, so converting it to a std::string
will not work. If you want to convert the value to a std::string
, and it is required for your application, use the required()
value semantic.
#include <boost/program_options.hpp>
namespace po = boost::program_options;
int main (int argc, char *argv[])
{
po::options_description config("Configuration");
config.add_options()
("IPAddress,i", po::value<std::string>()->required(), "IP Address")
("Port,p", po::value<std::string>()->required(), "Port")
;
try {
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, config),vm);
po::notify(vm);
std::cout << "Values" << std::endl;
const std::string address = vm["IPAddress"].as<std::string>();
const std::string port = vm["Port"].as<std::string>();
std::cout << "address: " << address << std::endl;
std::cout << "port: " << port << std::endl;
} catch ( const std::exception& e ) {
std::cerr << e.what() << std::endl;
return 1;
}
return 0;
}
Note the added catch block since parsing can (and will, as you have noticed) throw exceptions. Here is a sample session:
samm$ ./a.out
the option '--IPAddress' is required but missing
samm$ ./a.out --IPAddress 127.0.0.1
the option '--Port' is required but missing
samm$ ./a.out --IPAddress 127.0.0.1 --Port 5000
Values
address: 127.0.0.1
port: 5000
samm$
Here is an online demo showing the same behavior, courtesy of COmpile LInk RUn (coliru).
Not necessarily the same problem as this guy had but here's something that caught me:
If you put your type in an anonymous namespace, there will be two classes with the same name but different instances and the casting will fail. For example:
a.hpp:
namespace {
class MyClass {...};
}
b.cpp:
#include "a.hpp"
cli_options.add_options()("test", po::value<MyClass>(), "test desc");
c.cpp:
#include "a.hpp" // THIS WILL MAKE A DIFFERENT "MyClass"
vm["test"].as<MyClass>(); // Fails at runtime.
It fails because the MyClass
in b.cpp
and the one in c.cpp
aren't the same class. Because of the anonymous namespace.
Removing the anonymous namespace solves the problem.