我最近开始使用 boost::program_options
并发现它非常方便。也就是说,有一件事是我无法以一种好的方式编写自己的代码:
我想迭代在a中收集的所有选项 boost::program_options::variables_map
在屏幕上输出它们。这应该成为一个便利功能,我可以简单地调用列出所有设置的选项,而无需在添加新选项或每个程序时更新功能。
我知道我可以检查并输出各个选项,但如上所述,这应该成为一个普遍的解决方案,无视实际选项。我进一步知道我可以迭代的内容 variables_map
因为它只是一个扩展 std::map
。然后我可以检查存储中的类型包含 boost::any
变量和使用 .as<>
将其转换回适当的类型。但这意味着要编写一个长开关块,每种类型都有一个外壳。这对我来说看起来不像是好的编码风格。
所以问题是,是否有更好的方法来迭代这些选项并输出它们?
正如@Rost之前提到的,访客模式在这里是个不错的选择。要将其与PO一起使用,您需要使用通知符作为选项,如果传递选项,则通知符将填充您的选项中的条目 boost::variant
值。该集应单独存储。之后,您可以迭代您的设置并使用自动处理它们的操作(即打印) boost::apply_visitor
。
对于访客来说,继承自 boost::static_visitor<>
实际上,我使访问者和通用方法的使用范围更广。
我创造了一个 class MyOption
有描述, boost::variant
对于值和其他选项,如隐式,默认等。我填充了该类型的对象矢量 MyOption
就像PO那样为他们的选择做同样的事情(见 boost::po::options_add()
)通过模板。在传递的那一刻 std::string()
要么 double()
对于 boosts::varian
初始化你填写值的类型和其他东西,如默认,隐式。
之后我使用了访客模式填写 boost::po::options_description
容器自从 boost::po
需要自己的结构来解析输入命令行。在填充过程中,我为每个选项设置了通知器 - 如果它将被通过 boost::po
将自动填充我的原始对象 MyOption
。
接下来你需要执行 po::parse
和 po::notify
。之后,您将可以使用已填充的 std::vector<MyOption*>
通过访客模式,因为它持有boost :: variant里面。
所有这些都有什么好处 - 你必须在代码中只编写一次你的选项类型 - 填写你的 std::vector<MyOption*>
。
PS。如果使用这种方法,您将面临为没有值的选项设置notifyer的问题,请参阅此主题以获得解决方案: boost-program-options:没有值的选项的通知程序
PS2。代码示例:
std::vector<MyOptionDef> options;
OptionsEasyAdd(options)
("opt1", double(), "description1")
("opt2", std::string(), "description2")
...
;
po::options_descripton boost_descriptions;
AddDescriptionAndNotifyerForBoostVisitor add_decr_visitor(boost_descriptions);
// here all notifiers will be set automatically for correct work with each options' value type
for_each(options.begin(), options.end(), boost::apply_visitor(add_descr_visitor));
正如@Rost之前提到的,访客模式在这里是个不错的选择。要将其与PO一起使用,您需要使用通知符作为选项,如果传递选项,则通知符将填充您的选项中的条目 boost::variant
值。该集应单独存储。之后,您可以迭代您的设置并使用自动处理它们的操作(即打印) boost::apply_visitor
。
对于访客来说,继承自 boost::static_visitor<>
实际上,我使访问者和通用方法的使用范围更广。
我创造了一个 class MyOption
有描述, boost::variant
对于值和其他选项,如隐式,默认等。我填充了该类型的对象矢量 MyOption
就像PO那样为他们的选择做同样的事情(见 boost::po::options_add()
)通过模板。在传递的那一刻 std::string()
要么 double()
对于 boosts::varian
初始化你填写值的类型和其他东西,如默认,隐式。
之后我使用了访客模式填写 boost::po::options_description
容器自从 boost::po
需要自己的结构来解析输入命令行。在填充过程中,我为每个选项设置了通知器 - 如果它将被通过 boost::po
将自动填充我的原始对象 MyOption
。
接下来你需要执行 po::parse
和 po::notify
。之后,您将可以使用已填充的 std::vector<MyOption*>
通过访客模式,因为它持有boost :: variant里面。
所有这些都有什么好处 - 你必须在代码中只编写一次你的选项类型 - 填写你的 std::vector<MyOption*>
。
PS。如果使用这种方法,您将面临为没有值的选项设置notifyer的问题,请参阅此主题以获得解决方案: boost-program-options:没有值的选项的通知程序
PS2。代码示例:
std::vector<MyOptionDef> options;
OptionsEasyAdd(options)
("opt1", double(), "description1")
("opt2", std::string(), "description2")
...
;
po::options_descripton boost_descriptions;
AddDescriptionAndNotifyerForBoostVisitor add_decr_visitor(boost_descriptions);
// here all notifiers will be set automatically for correct work with each options' value type
for_each(options.begin(), options.end(), boost::apply_visitor(add_descr_visitor));
使用访客模式是一个很好的例子。不幸 boost::any
不支持访客模式 boost::variant
确实。不过还有一些第三方 方法。
另一个可能的想法是使用RTTI:创建地图 type_info
已知类型映射到类型处理程序仿函数。
我今天正在处理这类问题。这是一个老问题,但也许这将有助于那些正在寻找答案的人。
我想出的方法是尝试一堆<...>()然后忽略异常。它并不是非常漂亮,但我让它发挥作用。
在下面的代码块中,vm是来自boost program_options的variables_map。 vit是一个超过vm的迭代器,使它成为一对std :: string和boost :: program_options :: variable_value,后者是一个boost :: any。我可以用vit-> first打印变量的名称,但是vit-> second不是那么容易输出,因为它是一个boost :: any,即原始类型已经丢失。有些应该被转换为std :: string,有些应该被转换为double,依此类推。
所以,为了cout变量的值,我可以使用这个:
std::cout << vit->first << "=";
try { std::cout << vit->second.as<double>() << std::endl;
} catch(...) {/* do nothing */ }
try { std::cout << vit->second.as<int>() << std::endl;
} catch(...) {/* do nothing */ }
try { std::cout << vit->second.as<std::string>() << std::endl;
} catch(...) {/* do nothing */ }
try { std::cout << vit->second.as<bool>() << std::endl;
} catch(...) {/* do nothing */ }
我只有4种类型用于从命令行/配置文件中获取信息,如果我添加更多类型,我将不得不添加更多行。我承认这有点难看。
因为无论如何你只是打印它们,你可以在解析时获取原始字符串表示。 (可能代码中存在编译器错误,我将其从我的代码库中删除并且没有类型化的东西)
std::vector<std::string> GetArgumentList(const std::vector<boost::program_options::option>& raw)
{
std::vector<std::string> args;
BOOST_FOREACH(const boost::program_options::option& option, raw)
{
if(option.unregistered) continue; // Skipping unknown options
if(option.value.empty())
args.push_back("--" + option.string_key));
else
{
// this loses order of positional options
BOOST_FOREACH(const std::string& value, option.value)
{
args.push_back("--" + option.string_key));
args.push_back(value);
}
}
}
return args;
}
用法:
boost::program_options::parsed_options parsed = boost::program_options::command_line_parser( ...
std::vector<std::string> arguments = GetArgumentList(parsed.options);
// print