将boost::program_选项与std::optional一起使用

将boost::program_选项与std::optional一起使用,boost,boost-program-options,stdoptional,Boost,Boost Program Options,Stdoptional,Boost的program\u选项库现在Boost::optional,可以用std::optional实现同样的功能吗 我试图修改PR中的和代码,但两者似乎都不起作用 例如,整数的情况非常简单(在尝试模板专门化之前): 像validate(以及operator>)这样的问题通常是ª 您需要在一个关联的名称空间中声明重载。在这种情况下,由于int是一种基本类型,因此唯一关联的名称空间来自库代码: std对于可选,向量,字符串,分配器,字符特征(是的,这些都算上了!) boost用于any 您

Boost的
program\u选项
库现在Boost::optional,可以用
std::optional
实现同样的功能吗

我试图修改PR中的和代码,但两者似乎都不起作用

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


validate
(以及
operator>
)这样的问题通常是ª

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

  • std
    对于
    可选
    向量
    字符串
    分配器
    字符特征
    (是的,这些都算上了!)
  • boost
    用于
    any
您不希望在这些名称空间中添加代码,因为当库实现细节发生更改时,可能会干扰库函数或导致将来的中断

如果必须选择,您更愿意在这里选择
boost
,因为

  • 这就是提供现有功能的库
  • validate
    free函数被明确设计为一个定制点
旁注:留意-在库中构建自定义点的更好方法


修复 在所有这些废话之后,解决方案非常简单:

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

奖金 作为一个补充练习,让我们演示一下,如果可选值类型不是原语,而是在命名空间
MyLib
中声明的库类型,那么我们就没有上述大多数权衡:

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;
        }
    };
请参见

#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;
    }
}
#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;
    }
}


➣另请参见

一个额外的“章节”,展示如何通过利用ADL为您自己的名称空间提供更多的通用性来减少优惠。多好的回答啊。谢谢你回答我现在的问题和我即将提出的问题(我想下周)。
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;
        }
    };
    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
#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;
    }
}
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