//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"; string address = (vm["IPAddress"].as<std::string >()).c_str(); string port = (vm["Port"].as<std::string>()).c_str(); 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:
terminate called after throwing an instance of 'boost::exception_detail::clone_impl
' what(): boost::bad_any_cast: failed conversion using boost::any_cast
Program received signal SIGABRT, Aborted. 0x0000003afd835935 in raise () from /lib64/libc.so.6
string address = (vm["IPAddress"].as<std::string >()).c_str();is where the error occurs; I have tried std::string and string with the same results.
testboostpo -i 192.168.1.10 -p 5000is the command line.
I tried declaring the types, like so:
config.add_options() ("IPAddress,i", po::value<std::string>(), "IP Address") ("Port,p", po::value<std::string>(), "Port");but the error still occurred.
Could this be a genuine bug?
25 Answers
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).
0You 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") ; 2 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!
I had a similar error message, but it was because I was using the shorthand i, instead of IPAddress.
// this throws the cast exception
const std::string address = vm["i"].as<std::string>();
// this does not
const std::string address = vm["IPAddress"].as<std::string>();Boost takes the first one declared. So if your option is declared as IPAddress,i you need to use vm["IPAddres"], while i,IPAddress you need to use vm["i"].
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.