首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用boost::program_options与std::可选

使用boost::program_options与std::可选
EN

Stack Overflow用户
提问于 2021-03-09 02:02:33
回答 1查看 464关注 0票数 1

boost的program_options库现在支座 boost::可选,同样可以在std::optional上完成吗?

我试图同时修改文档示例和PR中的代码,但两者似乎都不起作用。

例如,对于整数非常简单的情况(在尝试模板专门化之前):

代码语言:javascript
运行
复制
void validate(boost::any& v, const std::vector<std::string>& values, std::optional<int>* target_type,
              int) {
  using namespace boost::program_options;
  validators::check_first_occurrence(v);
  const string& s = validators::get_single_string(values);

  int n = lexical_cast<int>(s);
  v = any(std::make_optional<int>(n));
}

目标类型不能istream的错误导致失败:

代码语言:javascript
运行
复制
external/boost/boost/lexical_cast/detail/converter_lexical.hpp:243:13: 
error: static_assert failed due to requirement 
'has_right_shift<std::__1::basic_istream<char>, std::__1::optional<int>, boost::binary_op_detail::dont_care>::value || boost::has_right_shift<std::__1::basic_istream<wchar_t>, std::__1::optional<int>, boost::binary_op_detail::dont_care>::value'
"Target type is neither std::istream`able nor std::wistream`able"
EN

Stack Overflow用户

回答已采纳

发布于 2021-03-09 14:14:11

validate (以及operator>> )这样的东西的问题通常是ADL1。

您需要在一个关联的命名空间中声明重载。在本例中,由于int是一个基本类型,因此唯一关联的命名空间来自库代码:

  • std for optionalvectorstringallocatorchar_traits (是的,这些都算数!)
  • boost for any

您不希望在这些命名空间中添加代码,因为当库实现细节发生变化时,您可能会干扰库函数或导致将来的破坏。

如果你不得不选择,你宁愿在这里选择boost,因为

  • 那就是提供现有功能的库
  • validate空闲函数被显式设计为自定义点。

Sidenote:密切关注tag_invoke --在库中构建定制点的更好方法

修正

在所有这些语句之后,解决方案非常简单:

代码语言:javascript
运行
复制
namespace boost::{
    void validate(boost::any& v, const std::vector<std::string>& values,
                  std::optional<int>*, int) {
        using namespace boost::program_options;
        validators::check_first_occurrence(v);
        const std::string& s = validators::get_single_string(values);

        int n = boost::lexical_cast<int>(s);
        v     = boost::any(std::make_optional<int>(n));
    }
} // namespace boost

添加两行使其工作:在Wandbox上直播

其他说明:

  1. 注入operator>>的“解决方案”一般不那么纯粹,因为
代码语言:javascript
运行
复制
- it has a potential to "infect" all other code with ADL-visible overloads that might interfere. Way more code uses `operator>>` than boost's `validate` function
- it thereby invites [UB](https://en.wikipedia.org/wiki/Undefined_behavior) due to [ODR](https://en.wikipedia.org/wiki/One_Definition_Rule) violations, when another translation unit, potentially legitimely, defines another `operator>>` for the same arguments.
  1. 在最近的编译器上,您可以使用vm.contains来代替稍微滥用的vm.count
  2. 还有一个不可流类型的问题,如果您定义了一个默认值,您可能还需要使用它指定字符串表示形式。

列表

编译器资源管理器的编译

代码语言:javascript
运行
复制
#include <boost/program_options.hpp>
#include <optional>
#include <iostream>

namespace po = boost::program_options;

namespace boost {
    void validate(boost::any& v, const std::vector<std::string>& values,
                std::optional<int>*, int) {
        using namespace boost::program_options;
        validators::check_first_occurrence(v);
        const std::string& s = validators::get_single_string(values);

        int n = boost::lexical_cast<int>(s);
        v     = boost::any(std::make_optional<int>(n));
    }
} // namespace boost

int main(int ac, char* av[]) {
    try {
        using Value = std::optional<int>;

        po::options_description desc("Allowed options");
        desc.add_options()
            ("help", "produce help message")
            ("value", po::value<Value>()->default_value(10, "10"),
                "value")
        ;

        po::variables_map vm;
        po::store(po::parse_command_line(ac, av, desc), vm);
        po::notify(vm);

        if (vm.contains("value")) {
            std::cout << "value is " << vm["value"].as<Value>().value() << "\n";
        }

    } catch (std::exception& e) {
        std::cout << e.what() << "\n";
        return 1;
    }
}

奖金

作为一个附加的练习,让我们演示一下,如果您的可选value_type不是,不是,而是在名称空间MyLib中声明的库类型,那么我们就没有上面提到的大部分权衡:

代码语言:javascript
运行
复制
namespace MyLib {
    template <typename T> struct MyValue {
        MyValue(T v = {}) : value(std::move(v)) {}

      private:
        T value;
        friend std::istream& operator>>(std::istream& is, MyValue& mv) {
            return is >> mv.value;
        }
        friend std::ostream& operator<<(std::ostream& os, MyValue const& mv) {
            return os << mv.value;
        }
    };

现在,您可以为MyLib命名空间中的任何类型提供通用验证器,无论它是可选的还是非可选的,并让ADL通过您的MyLib命名空间找到它们:

代码语言:javascript
运行
复制
    template <typename T, typename Values>
    void validate(boost::any& v, Values const& values, T*, int) {
        po::validators::check_first_occurrence(v);
        v = boost::lexical_cast<T>(
                po::validators::get_single_string(values));
    }

    template <typename T, typename Values>
    void validate(boost::any& v, Values const& values, std::optional<T>*, int) {
        po::validators::check_first_occurrence(v);
        v = std::make_optional(
                boost::lexical_cast<T>(
                    po::validators::get_single_string(values)));
    }
} // namespace MyLib

请参阅现场演示

代码语言:javascript
运行
复制
#include <boost/program_options.hpp>
#include <iostream>
#include <iomanip>

namespace po = boost::program_options;

namespace MyLib {
    template <typename T> struct MyValue {
        MyValue(T v = {}) : value(std::move(v)) {}

      private:
        T value;
        friend std::istream& operator>>(std::istream& is, MyValue& mv) {
            return is >> std::boolalpha >> mv.value;
        }
        friend std::ostream& operator<<(std::ostream& os, MyValue const& mv) {
            return os << std::boolalpha << mv.value;
        }
    };

    // Provide generic validators for any types in your MyLib namespace, be it
    // optional or not
    template <typename T, typename Values>
    void validate(boost::any& v, Values const& values, T*, int) {
        po::validators::check_first_occurrence(v);
        v = boost::lexical_cast<T>(
                po::validators::get_single_string(values));
    }

    template <typename T, typename Values>
    void validate(boost::any& v, Values const& values, std::optional<T>*, int) {
        po::validators::check_first_occurrence(v);
        v = std::make_optional(
                boost::lexical_cast<T>(
                    po::validators::get_single_string(values)));
    }
} // namespace MyLib

int main(int ac, char* av[]) {
    try {
        using Int    = MyLib::MyValue<int>;
        using OptInt = std::optional<MyLib::MyValue<int>>;
        using OptStr = std::optional<MyLib::MyValue<std::string> >;

        po::options_description desc("Allowed options");
        desc.add_options()
            ("ival", po::value<Int>()->default_value(Int{10}),
                  "integer value")
            ("opti", po::value<OptInt>()->default_value(OptInt{}, "(nullopt)"),
                  "optional integer value")
            ("sval", po::value<OptStr>()->default_value(OptStr{"secret"}, "'secret'"),
                  "optional string value")
        ;

        po::variables_map vm;
        po::store(po::parse_command_line(ac, av, desc), vm);
        po::notify(vm);

        std::cout << "Options: " << desc << "\n";

        if (vm.contains("ival")) {
            std::cout << "ival is " << vm["ival"].as<Int>() << "\n";
        }
        if (vm.contains("opti")) {
            if (auto& v = vm["opti"].as<OptInt>())
                std::cout << "opti is " << v.value() << "\n";
            else
                std::cout << "opti is nullopt\n";
        }
        if (vm.contains("sval")) {
            if (auto& v = vm["sval"].as<OptStr>())
                std::cout << "sval is " << v.value() << "\n";
            else
                std::cout << "sval is nullopt\n";
        }
    } catch (std::exception& e) {
        std::cout << e.what() << "\n";
        return 1;
    }
}

对于./a.out --ival=42 --sval=LtUaE打印:

代码语言:javascript
运行
复制
Options: Allowed options:
  --ival arg (=10)        integer value
  --opti arg (=(nullopt)) optional integer value
  --sval arg (='secret')  optional string value

ival is 42
opti is nullopt
sval is LtUaE

也见为什么Boost在“程序选项”中使用全局函数覆盖来实现自定义验证器

票数 2
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66539770

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档