简体   繁体   English

使用boost程序选项时如何解决“boost::bad_any_cast:使用boost::any_cast转换失败”的问题?

[英]How to solve “boost::bad_any_cast: failed conversion using boost::any_cast” when using boost program options?

//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:这是 gdb 输出,似乎是强制转换问题:

terminate called after throwing an instance of 'boost::exception_detail::clone_impl在抛出 'boost::exception_detail::clone_impl 实例后调用终止

' what(): boost::bad_any_cast: failed conversion using boost::any_cast ' what(): boost::bad_any_cast: 使用 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.我尝试过 std::string 和 string 的结果相同。

testboostpo -i 192.168.1.10 -p 5000

is 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?这可能是一个真正的错误吗?

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.您会看到从po::variables_map抛出的boost::bad_any_cast异常,因为po::options_description_easy_init::operator()的两个const char*参数重载未指定po::value_semantic类型,因此将其转换为std::string不起作用。 If you want to convert the value to a std::string , and it is required for your application, use the required() value semantic.如果要将值转换为std::string ,并且您的应用程序需要它,请使用required()值语义。

#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.请注意添加的 catch 块,因为解析可以(并且会,正如您所注意到的)抛出异常。 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).这是一个在线演示,展示了相同的行为,由编译链接运行 (coliru) 提供。

You need to declare the ip-address and port as strings when you add the options:添加选项时,您需要将 ip-address 和 port 声明为字符串:

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. Sam 的解决方案指出了必需的参数,而 OP 的代码建议需要 - 只需将它们标记为必需。 For optional inputs, the Boost PO tutorial gives us a template for checking if the option exists before converting it:对于可选输入, Boost PO 教程为我们提供了一个模板,用于在转换之前检查该选项是否存在:

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!我的问题 - 我已经复制/粘贴并忘记将 if 测试与用法对齐!

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: a.hpp:

namespace {
class MyClass {...};
}

b.cpp: b.cpp:

#include "a.hpp"
cli_options.add_options()("test", po::value<MyClass>(), "test desc");

c.cpp: 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.它失败,因为MyClassb.cpp和一个在c.cpp是不一样的类。 Because of the anonymous namespace.因为匿名命名空间。

Removing the anonymous namespace solves the problem.删除匿名命名空间可以解决问题。

I had a similar error message, but it was because I was using the shorthand i , instead of IPAddress .我有一个类似的错误消息,但这是因为我使用的是速记i ,而不是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. Boost 取第一个声明的。 So if your option is declared as IPAddress,i you need to use vm["IPAddres"] , while i,IPAddress you need to use vm["i"] .因此,如果您的选项声明为IPAddress,i您需要使用vm["IPAddres"] ,而i,IPAddress您需要使用vm["i"]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM