boost::program_options 验证每个参数而不是每个参数类型?
boost::program_options validation per argument instead of per argument type?
boost::program_options
似乎支持某种程度的 custom validation 但我觉得奇怪的是,验证是根据类型而不是每个参数编写的,我想知道我是否在这里遗漏了一些东西。
例如,假设您有一个 "file editing" 程序,它从命令行获取 input
和 output
文件名。接下来假设您要将这些存储到 boost::filesystem::path
类型的变量中。现在,假设我们要求 input
文件 必须 存在,但 output
文件不存在(即如果 output
文件不存在,我们将创建它。)理想情况下,我们有一种方法可以测试 input
参数是否存在,并且单独测试 output
文件是否存在且可写或位于我们可以写入的目录。 (这里的具体差异实际上并不相关。这适用于您在多个地方使用相同类型的任何情况,您希望根据用途使用不同的验证规则。)
因为我们通过创建 validate
的覆盖来配置验证器(这可能是在编译时通过它们的类型签名找到的,看来我们只能为 boost::filesystem::path
的所有实例使用一个验证器。我已经看到了 notify
钩子,但是那些回调的签名有一个 const
限定符,所以你似乎不能修改这个值,文档中也不清楚 throw
ing 在 notify
回调中将进入验证系统。
这似乎是一个非常基本的限制,我认为我可能遗漏了一些东西。我是吗?想法?
您可以将每个选项的验证逻辑放入 notifier 回调中。
程序中的每个概念都有一个类型永远不会有坏处。如果两条路径代表不同的概念,给他们自己的类型:
#include <iostream>
#include <cstdlib>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
namespace po = boost::program_options;
namespace fs = boost::filesystem;
template<class Type, class Tag>
struct tagged
{
Type const& get() const { return value_; }
Type& get() { return value_; }
operator Type& () { get(); }
friend decltype(auto) operator>>(std::istream& is, tagged& t) {
return is >> t.get();
}
friend decltype(auto) operator<<(std::ostream& os, tagged const& t) {
return os << t.get();
}
// and so on...
Type value_;
};
using bar_path = tagged<fs::path, class bar_path_tag>;
using foo_path = tagged<fs::path, class foo_path_tag>;
int main()
{
bar_path bar;
foo_path foo;
po::options_description desc("prog");
desc.add_options()
("foo", po::value(&foo), "a foo path")
("bar", po::value(&bar), "a bar path")
;
}
boost::program_options
似乎支持某种程度的 custom validation 但我觉得奇怪的是,验证是根据类型而不是每个参数编写的,我想知道我是否在这里遗漏了一些东西。
例如,假设您有一个 "file editing" 程序,它从命令行获取 input
和 output
文件名。接下来假设您要将这些存储到 boost::filesystem::path
类型的变量中。现在,假设我们要求 input
文件 必须 存在,但 output
文件不存在(即如果 output
文件不存在,我们将创建它。)理想情况下,我们有一种方法可以测试 input
参数是否存在,并且单独测试 output
文件是否存在且可写或位于我们可以写入的目录。 (这里的具体差异实际上并不相关。这适用于您在多个地方使用相同类型的任何情况,您希望根据用途使用不同的验证规则。)
因为我们通过创建 validate
的覆盖来配置验证器(这可能是在编译时通过它们的类型签名找到的,看来我们只能为 boost::filesystem::path
的所有实例使用一个验证器。我已经看到了 notify
钩子,但是那些回调的签名有一个 const
限定符,所以你似乎不能修改这个值,文档中也不清楚 throw
ing 在 notify
回调中将进入验证系统。
这似乎是一个非常基本的限制,我认为我可能遗漏了一些东西。我是吗?想法?
您可以将每个选项的验证逻辑放入 notifier 回调中。
程序中的每个概念都有一个类型永远不会有坏处。如果两条路径代表不同的概念,给他们自己的类型:
#include <iostream>
#include <cstdlib>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
namespace po = boost::program_options;
namespace fs = boost::filesystem;
template<class Type, class Tag>
struct tagged
{
Type const& get() const { return value_; }
Type& get() { return value_; }
operator Type& () { get(); }
friend decltype(auto) operator>>(std::istream& is, tagged& t) {
return is >> t.get();
}
friend decltype(auto) operator<<(std::ostream& os, tagged const& t) {
return os << t.get();
}
// and so on...
Type value_;
};
using bar_path = tagged<fs::path, class bar_path_tag>;
using foo_path = tagged<fs::path, class foo_path_tag>;
int main()
{
bar_path bar;
foo_path foo;
po::options_description desc("prog");
desc.add_options()
("foo", po::value(&foo), "a foo path")
("bar", po::value(&bar), "a bar path")
;
}