boost::program_options:迭代并打印所有选项
boost::program_options : iterating over and printing all options
我最近开始使用boost::program_options
,发现它非常方便。也就是说,有一件事我无法用一种好的方式给自己编码:
我想迭代boost::program_options::variables_map
中收集的所有选项,将它们输出到屏幕上。这应该成为一个方便的函数,我可以简单地调用它来列出所有设置的选项,而无需在添加新选项或为每个程序更新函数。
我知道我可以检查和输出单个选项,但如上所述,这应该成为一个忽略实际选项的通用解决方案。我进一步知道我可以迭代variables_map
的内容,因为它只是一个扩展的std::map
。然后,我可以检查存储的boost::any
变量中包含的类型,并使用.as<>
将其转换回适当的类型。但这意味着编码一个长的开关块,每种类型都有一个案例。在我看来,这不是一种好的编码风格
所以问题是,有没有更好的方法来迭代这些选项并输出它们?
正如@Rost之前提到的,访问者模式是一个不错的选择。要将其与PO一起使用,您需要为您的选项使用通知程序,这样,如果传递了选项,通知程序将在您的boost::variant
值集中填充一个条目。该套装应单独存放。之后,您可以迭代您的集合,并使用boost::apply_visitor
自动处理它们上的操作(即打印)。
对于访客,从boost::static_visitor<>
继承
实际上,我使Visitor和泛型方法的使用范围更广。
我创建了一个class MyOption
,它包含描述,boost::variant
代表值和其他选项,如隐式、默认等。我通过模板填充MyOption
类型的对象向量,就像PO填充它们的选项一样(参见boost::po::options_add()
)。在传递std::string()
或double()
进行boosts::varian
t初始化时,您可以填充值的类型和其他内容,如默认值、隐式。
之后,我使用Visitor模式填充boost::po::options_description
容器,因为boost::po
需要自己的结构来解析输入命令行。在填充过程中,我为每个选项设置了notifyer——如果它将被传递,boost::po
将自动填充我的原始对象MyOption
。
接下来,您需要执行po::parse
和po::notify
。之后,您将能够通过Visitor模式使用已经填充的std::vector<MyOption*>
,因为它内部包含boost::variant。
所有这些的好处是——在填充std::vector<MyOption*>
时,您只需在代码中编写一次选项类型。
PS。如果使用这种方法,您将面临为无值选项设置notifyer的问题,请参阅本主题以获得解决方案:boost程序选项:无值选项的notifyer
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));
使用Visitor模式是一个很好的例子。不幸的是,boost::any
不像boost::variant
那样支持访问者模式。尽管如此,还是有一些第三方方法。
另一个可能的想法是使用RTTI:创建映射到类型处理函数的已知类型的type_info
的映射。
由于您无论如何都要打印它们,所以在解析时可以获取原始字符串表示。(很可能代码中有编译器错误,我把它从我的代码库中撕了出来,并对一堆东西进行了未键入的修改)
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
我今天正处理这类问题。这是一个古老的问题,但也许这将帮助那些正在寻找答案的人。
我想出的方法是尝试一堆作为<…>(),然后忽略该异常。它不是很漂亮,但我已经做好了。
在下面的代码块中,vm是来自boost program_options的variables_map。vit是vm上的一个迭代器,使其成为一对std::string和boost::program_options::variable_value,后者是boost::any。我可以用vit->first打印变量的名称,但vit->second不太容易输出,因为它是一个boost::any,即原始类型已丢失。有些应该被强制转换为std::字符串,有些应该被转换为double,依此类推
所以,为了计算变量的值,我可以使用这个:
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种类型可以用来从命令行/config文件中获取信息,如果我添加更多的类型,我将不得不添加更多的行。我承认这有点难看。
- 如何循环打印顶点结构
- 为什么在popback()操作之后,它仍然打印完整的矢量
- 如何在选项卡视图Qt中设置一个新项目,并保存以前的项目
- Win32编译器选项和内存分配
- 如何在c++中打印目录
- 有一个打印语句的函数是一种糟糕的编程实践吗
- 在线编译器中的分段C++没有打印消息
- 在C++中打印指向不同基元数据类型的指针的内存地址
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值
- 如何将结构插入到集合中并打印集合的成员
- 在循环C++中指定字符串之后,不会打印该字符串
- 以螺旋方式打印矩阵的程序.(工作不好)
- 从控制台中删除最后打印的元素
- C/C++预处理器是否可以检测一些编译器选项
- gdb在选项卡完成的情况下打印变量时冻结
- boost::program_options:迭代并打印所有选项
- 从C++代码设置高级打印选项
- 是否有用于打印源文件直接包含的标题的 GCC 选项
- 用cout打印出布尔选项
- 用c++编写一个菜单程序,让用户从选项列表中进行选择,如果输入的不是其中一个选项,则重新打印该列表