我最近开始使用boost::program_options
,发现它非常方便。也就是说,有一件事遗漏,我无法以一种好的方式编码自己:
我想迭代在boost::program_options::variables_map
中收集的所有选项,以便在屏幕上输出它们。这应该成为一个便利功能,我可以简单地调用列出所有设置的选项,而无需在添加新选项或每个程序时更新功能。
我知道我可以检查并输出各个选项,但如上所述,这应该成为一个对实际选项无视的通用解决方案。我进一步知道我可以迭代variables_map
的内容,因为它只是一个扩展的std::map
。然后,我可以检查存储的boost::any
变量中的类型包含,并使用.as<>
将其转换回适当的类型。但这意味着要编写一个长开关块,每种类型都有一个外壳。对我来说,这看起来不是很好的编码风格。
所以问题是,是否有更好的方法来迭代这些选项并输出它们?
答案 0 :(得分:6)
正如@Rost之前提到的,访客模式在这里是个不错的选择。要将其与PO一起使用,您需要为选项使用通知程序,以便在传递选项时,通知程序将填充boost::variant
值集中的条目。该集应单独存储。之后,您可以迭代您的设置并使用boost::apply_visitor
自动处理它们上的操作(即打印)。
对于访问者,继承自boost::static_visitor<>
实际上,我使访问者和通用方法的使用范围更广。
我创建了一个class MyOption
,其中包含值boost::variant
以及其他选项(如隐式,默认等)。我以类似于PO的方式填充MyOption
类型的对象向量,通过模板选择它们(参见boost::po::options_add()
)。在为std::string()
t初始化传递double()
或boosts::varian
时,您需要填写值的类型以及默认值,隐式等其他内容。
之后我使用Visitor模式填充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: notifier for options with no value
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));
答案 1 :(得分:5)
使用访客模式是一个很好的例子。很遗憾,boost::any
不支持boost::variant
之类的访问者模式。然而,有一些第三方approaches。
另一个可能的想法是使用RTTI:创建映射到处理函数函数的已知类型type_info
的映射。
答案 2 :(得分:0)
我今天正在处理这类问题。这是一个老问题,但也许这将有助于那些正在寻找答案的人。
我想出的方法是尝试一堆as&lt; ...&gt;()然后忽略异常。它并不是非常漂亮,但我让它发挥作用。
在下面的代码块中,vm是来自boost program_options的variables_map。 vit是一个超过vm的迭代器,使它成为一对std :: string和boost :: program_options :: variable_value,后者是一个boost :: any。我可以先用vit-&gt;打印变量的名称,但是vit-&gt; 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种类型用于从命令行/配置文件中获取信息,如果我添加了更多类型,我将不得不添加更多行。我承认这有点难看。
答案 3 :(得分:0)
因为你打算只打印它们,所以你可以在解析时获取原始字符串表示。 (可能代码中存在编译器错误,我把它从我的代码库中删除并且没有类型化的东西)
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