boost :: program_options:迭代并打印所有选项

时间:2012-09-03 08:22:57

标签: c++ boost command-line boost-program-options

我最近开始使用boost::program_options,发现它非常方便。也就是说,有一件事遗漏,我无法以一种好的方式编码自己:

我想迭代在boost::program_options::variables_map中收集的所有选项,以便在屏幕上输出它们。这应该成为一个便利功能,我可以简单地调用列出所有设置的选项,而无需在添加新选项或每个程序时更新功能。

我知道我可以检查并输出各个选项,但如上所述,这应该成为一个对实际选项无视的通用解决方案。我进一步知道我可以迭代variables_map的内容,因为它只是一个扩展的std::map。然后,我可以检查存储的boost::any变量中的类型包含,并使用.as<>将其转换回适当的类型。但这意味着要编写一个长开关块,每种类型都有一个外壳。对我来说,这看起来不是很好的编码风格。

所以问题是,是否有更好的方法来迭代这些选项并输出它们?

4 个答案:

答案 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::parsepo::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