使用boost :: optional来提升program_options

时间:2018-04-23 06:09:49

标签: c++ boost

我正在尝试使用constructor() { // Require the elliptic library for curve cryptography var EC = require('elliptic').ec; var ec = new EC('secp256k1'); this.ec = ec; } importPublicKey(x, y) { var pub = { x: x.toString('hex'), y: y.toString('hex') }; var key = this.ec.keyFromPublic(pub, 'hex'); this.key = key; return key; } verify(message, signature) { return this.key.verify(message, signature); } 向使用大量boost::program_options参数的现有代码库编写CLI,因此我想从命令行解析boost::optional。如果未指定,则结果为boost::optional,如果指定,则获得初始化值。

当我尝试使用自定义Boost验证器执行此操作时,我得到boost::none。以下是问题的MCVE。

我有一个班级

bad_any_cast

和这个类的自定义Boost验证器。这种验证器风格直接取自升级文档。

class MyClass {                                                                                                     
public:                                                                                                       
    int x;                                                                                                    
    MyClass(const int a) : x(a) {};                                                                                                                                                                                                                                                                                              
};    

最后,我的主要功能,它创建一个简单的解析器。

void validate(boost::any& v, const std::vector<std::string>& values,                                          
          MyClass* target_type, int) {                                                                          

    v = boost::any(boost::optional<MyClass>(boost::in_place(1)));                                                   
}

如果我没有在命令行上传递#include <boost/program_options.hpp> #include <boost/optional.hpp> #include <boost/optional/optional_io.hpp> #include <boost/utility/in_place_factory.hpp> int main(int argc, char** argv) { po::options_description desc(""); desc.add_options() ("MyClass", po::value<boost::optional<MyClass>>()->default_value(boost::none, ""), "MyClass"); po::variables_map args; po::store(po::parse_command_line(argc, argv, desc), args); } 选项,则代码会成功运行。但是,如果我通过--MyClass选项,则会收到--MyClass

bad_any_cast

我介入了GDB,这被归入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 MyClass,但如果我在any_cast之外编写类似的代码,它就会成功。

例如,以下代码将相同的boost::program_options转换为boost::optional然后将其强制转换,运行时没有错误。

boost::any

我知道#include <iostream> #include <boost/program_options.hpp> #include <boost/optional.hpp> #include <boost/optional/optional_io.hpp> #include <boost/utility/in_place_factory.hpp> namespace po = boost::program_options; class MyClass { public: int x; MyClass(const int a) : x(a) {}; }; int f(boost::any& v) { v = boost::any(boost::optional<MyClass>(boost::in_place(1))); } int main(int argc, char** argv) { boost::any v; f(v); boost::any_cast<boost::optional<MyClass>>(v); } 支持program_options所以我可以使用if语句在解析后将基值包装在一个可选项中,但我认为使用自定义验证器方法实现它会更加清晰上方。

有没有人对如何解决此问题有任何想法或建议?

1 个答案:

答案 0 :(得分:2)

验证功能不是可选的。类型参数(target_type)为MyClass*而非optional<MyClass>*这一事实暗示了这一点。 docs

  

该功能需要四个参数。第一个是值的存储,在这种情况下是空的或包含magic_number类的实例。第二个是在下一次出现的选项中找到的字符串列表。需要剩下的两个参数来解决某些编译器缺少部分模板特化和部分函数模板排序的问题。

这是我的看法:

<强> Live On Coliru

#include <boost/optional.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/program_options.hpp>
#include <boost/utility/in_place_factory.hpp>
#include <iostream>
#include <string>

namespace po = boost::program_options;

struct MyClass {
    int x;
    MyClass(int a) : x(a){};

    friend std::ostream& operator<<(std::ostream& os, MyClass const& mc) {
        return os << "MyClass(" << mc.x << ")";
    }
};

void validate(boost::any &v, const std::vector<std::string> &values, MyClass * /*target_type*/, int) {
    v = MyClass(std::stoi(values.front()));
}

int main(int argc, char **argv) {
    po::options_description desc("");
    desc.add_options()("MyClass", po::value<boost::optional<MyClass> >()->default_value(boost::none, ""), "MyClass");

    po::variables_map args;
    store(parse_command_line(argc, argv, desc), args);
    notify(args);

    std::cout << "Arg: " << args["MyClass"].as<boost::optional<MyClass> >() << "\n";
}

有关:

./a.out --MyClass 42
./a.out --MyClass no_chill
./a.out

打印

+ ./a.out --MyClass 42
Arg:  MyClass(42)
+ ./a.out --MyClass no_chill
terminate called after throwing an instance of 'std::invalid_argument'
  what():  stoi
+ ./a.out
Arg: --

加成

the docs获取提示我认为你可以使它更优雅:

int main(int argc, char **argv) {
    po::options_description desc("");

    boost::optional<MyClass> my_option;
    desc.add_options()("MyClass", po::value(&my_option), "MyClass");

    po::variables_map args;
    store(parse_command_line(argc, argv, desc), args);
    notify(args);

    std::cout << "Arg: " << my_option << "\n";
}

删除所有类型和名称的错误重复。

<强> Live On Coliru

仍然是相同的输出。