覆盖boost中的validate方法不能使用样式集

时间:2018-06-05 20:02:15

标签: c++ boost

我试图覆盖boost::program_options中的validate方法,以便解析虚线的ip地址。我还需要在选项前用一个破折号解析参数。为此,我设置了标志po::command_line_style::allow_slash_for_short。当我添加标志时,验证方法不会被调用。如果我没有设置它工作正常。有人可以指出问题或建议解决方法吗?

这是我的代码:

struct in_addr MCAST_ADDR;

void validate(boost::any& v, const std::vector<std::string>& values, in_addr* target_type, int)
{
    printf("validate\n");
    std::string const& mcast_addr = boost::program_options::validators::get_single_string(values);

    if (!mcast_addr.empty())
        if (inet_aton(mcast_addr.c_str(), &MCAST_ADDR) == 0) {
        fprintf(stderr, "Invalid address!\n");
    }
    v = boost::any(MCAST_ADDR);
}

namespace po = boost::program_options;

void parse_args(int argc, char *argv[]) {

    try
    {
        po::options_description desc("Options:");
        desc.add_options()
            ("help,h", "Help screen")
            ("a", po::value<in_addr>()->required(), "MCAST_ADDR");

        po::variables_map vm;
        po::store(po::command_line_parser(argc, argv)
                      .options(desc).
                      .style(po::command_line_style::allow_slash_for_short) /* the problematic line */
                      .run(), vm);

        po::notify(vm);

    }
    catch (const po::error &ex) {
        std::cerr << ex.what() << '\n';
    }
}

int main(int argc, char *argv[]) {
    parse_args(argc, argv);
}

1 个答案:

答案 0 :(得分:1)

我认为一般问题是您的选项没有简短的名称。是的,&#34;长名&#34;只是一个字符,但这仍然被认为是长名。

要允许将长选项伪装为短选项,请使用allow_long_disguise

接下来,要以任何方式允许短选项,您必须决定如何呈现选项参数(--a=value--a value)。

您会发现您未能做出此选择,因为有时库会为您提供运行时诊断。 E.g:

.style(po::command_line_style::allow_long_disguise | po::command_line_style::allow_slash_for_short)

导致:

  • boost::program_options misconfiguration: choose one or other of 'command_line_style::long_allow_next' (whitespace separated arguments) or 'command_line_style::long_allow_adja cent' ('=' separated arguments) for long options.

现在,解决这个问题的最简单方法就是建立默认行为:

.style(po::command_line_style::default_style
    | po::command_line_style::allow_long_disguise
    | po::command_line_style::allow_slash_for_short)

现在你可以传递大量的选择:

./a.out /a 239.10.11.12
./a.out /a=239.10.11.12
./a.out -a 239.10.11.12
./a.out -a=239.10.11.12
./a.out --a 239.10.11.12
./a.out --a=239.10.11.12

全部打印

Parsed: 239.10.11.12

奖金

让我们使用Boost来解析和验证IP地址!

using ip_address = boost::asio::ip::address;

namespace boost { namespace asio { namespace ip {
    void validate(boost::any& v, const std::vector<std::string>& values, ip_address* /*target_type*/, int) {
        std::string const& mcast_addr = boost::program_options::validators::get_single_string(values);

        auto address = ip_address::from_string(mcast_addr);

        if (!address.is_multicast())
            throw std::invalid_argument("address not multicast: " + address.to_string());

        v = address;
    }
} } }

这样,如果我们传递非多播地址,我们会将其标记为错误并报告:

terminate called after throwing an instance of 'std::invalid_argument'
  what():  address not multicast: 240.10.11.12

完整演示

<强> Live On Coliru

#include <boost/asio/ip/address.hpp>
#include <boost/program_options.hpp>
#include <iostream>

using ip_address = boost::asio::ip::address;

namespace boost { namespace asio { namespace ip {
    void validate(boost::any& v, const std::vector<std::string>& values, ip_address* /*target_type*/, int) {
        std::string const& mcast_addr = boost::program_options::validators::get_single_string(values);

        auto address = ip_address::from_string(mcast_addr);

        if (!address.is_multicast())
            throw std::invalid_argument("address not multicast: " + address.to_string());

        v = address;
    }
} } }

namespace po = boost::program_options;

int main(int argc, char *argv[])  try {
    po::options_description desc("Options:");
    desc.add_options()
        ("help,h", "Help screen")
        ("a", po::value<ip_address>()->required(), "MCAST_ADDR")
        ;

    po::variables_map vm;
    po::store(po::command_line_parser(argc, argv)
            .options(desc)
            .style(po::command_line_style::default_style
                | po::command_line_style::allow_long_disguise
                | po::command_line_style::allow_slash_for_short)
            .run(), vm);

    po::notify(vm);

    std::cout << "Parsed: " << vm["a"].as<ip_address>() << std::endl;

} catch (const po::error &ex) {
    std::cerr << ex.what() << std::endl;
}

其中还显示了所有测试用例:

+ ./a.out /a 239.10.11.12
Parsed: 239.10.11.12
+ ./a.out /a=239.10.11.12
Parsed: 239.10.11.12
+ ./a.out -a 239.10.11.12
Parsed: 239.10.11.12
+ ./a.out -a=239.10.11.12
Parsed: 239.10.11.12
+ ./a.out --a 239.10.11.12
Parsed: 239.10.11.12
+ ./a.out --a=239.10.11.12
Parsed: 239.10.11.12

+ ./a.out /a 240.10.11.12
terminate called after throwing an instance of 'std::invalid_argument'
  what():  address not multicast: 240.10.11.12
bash: line 11: 31582 Aborted                 (core dumped) ./a.out $options
+ ./a.out /a=240.10.11.12
terminate called after throwing an instance of 'std::invalid_argument'
  what():  address not multicast: 240.10.11.12
bash: line 11: 31585 Aborted                 (core dumped) ./a.out $options
+ ./a.out -a 240.10.11.12
terminate called after throwing an instance of 'std::invalid_argument'
  what():  address not multicast: 240.10.11.12
bash: line 11: 31587 Aborted                 (core dumped) ./a.out $options
+ ./a.out -a=240.10.11.12
terminate called after throwing an instance of 'std::invalid_argument'
  what():  address not multicast: 240.10.11.12
bash: line 11: 31589 Aborted                 (core dumped) ./a.out $options
+ ./a.out --a 240.10.11.12
terminate called after throwing an instance of 'std::invalid_argument'
  what():  address not multicast: 240.10.11.12