boost::program_options::notify() 是用来做什么的?

45
这个问题涉及到C++ Boost program_options库。
所有的教程都很清楚,我应该在完成变量映射后调用notify(),但我不确定这对我有什么作用。注释掉它似乎没有任何影响,并且文档没有详细介绍: http://www.boost.org/doc/libs/1_47_0/doc/html/boost/program_options/notify.html 其他来源表明它运行“用户定义”的函数。如果是这样,这些函数如何注册,它们做什么?它们可能会抛出异常吗?
3个回答

32

notify()value_semantic的成员函数。它是一个钩子,提供了一种方式,使得一旦确定选项的最终值,就可以自动执行任何应该执行的操作,并将其封装在自己的函数中。这样可以防止代码拥有一个长函数,对每个选项进行操作。随着可能的选项增加,这种过程式代码可能会变得难以控制。

您可以在Boost手册中看到设置通知函数的示例

options_description desc;
desc.add_options()
    ("compression", value<int>()->default_value(10), "compression level")
    ("email", value< vector<string> >()
        ->composing()->notifier(&your_function), "email")
    ;

这些声明指定第一个选项的默认值为10,第二个选项可以出现多次且所有实例应合并,并且在解析完成后,库将调用函数"your_function",并将"email"选项的值作为参数传递。

哦,我现在明白了。你必须寻找“notifier”,而不是“notify”。notify函数传递了一个const引用到值,所以不能改变它?我看不出除了如果选项“错误”就抛出异常之外还能做什么。 - olooney
@olooney: 意图是你使用该选项进行预期的操作。例如,如果您有一个可以更改搜索路径的选项,则您的通知函数将修改搜索路径。正如我在我的答案中所提到的,您可以在选项解析代码中执行相同的逻辑,通过逐个检查每个选项,然后执行某些操作,但这可能会导致长、难以阅读或修改的过程性块。 - Conspicuous Compiler
2
当然可以,但是如果没有变异variable_map的能力,将不透明句柄传递给环境对象或使用函数对象(例如boost :: function)进行绑定,那么您真正限制了异常和全局副作用。这仍然对于更改工作目录或设置全局“详细”标志非常有用,但它不足以将大部分选项解析移动到通知程序中。也许我应该在做出判断之前先尝试一下,我只是在理论上推测。 - olooney
我在这里链接到boost源代码:https://dev59.com/Zmw05IYBdhLWcg3wnjPC#66865258。除了你的答案之外,查看源代码也对我有所帮助。 - Gabriel Staples

6

当你提到“函数对象”时,我认为你走在了正确的轨道上...

一种常见的处理选项的方式是将其参数传递给某个对象的方法。如果您可以将该方法包装成一个notifier()接受的参数,则可以更直接地使用notifiers进行处理。而且您确实可以这样做。(如果boost::function有这样做的方法,我不太熟悉它(现在也太懒得去学习它) - 以下使用STDLIB中头文件functional中的例程。)

例如:

您的其中一个选项是--config-file,接受一个字符串参数,指定非默认配置文件的路径。您有一个名为ConfigParser的类。没有notifiers,您的代码可能如下所示:

ConfigParser *cp = new ConfigParser();
std::string cp_path;
desc.add_options()
    ("config-file", value<std::string>(&cp_path)->default_value("~/.myconfig"), "Config File")
    // ... the rest of your options
    ;

cp->setPath(cp_path);

使用通知器:

#include <functional>

ConfigParser *cp = new ConfigParser();
desc.add_options()
    ("config-file", value<std::string>()->default_value("~/.myconfig")->notifier(std::bind1st(std::mem_fun(&ConfigParser::setPath), cp)), "Config File")
    // ... the rest of your options
    ;

2

我喜欢直接查看源代码。所以,这里是链接:https://github.com/boostorg/program_options/blob/develop/include/boost/program_options/variables_map.hpp#L52

/** Runs all 'notify' function for options in 'm'. */
BOOST_PROGRAM_OPTIONS_DECL void notify(variables_map& m);

因此,它会运行所有variables_mapnotify()函数,mstd::map<std::string,variable_value>的包装器,并包含从命令选项(作为std::string)到variable_value的映射(任何类型)。

现在这个由@Conspicuous Compiler提供的答案有更多的上下文信息,所以请阅读它。查看他的答案以及源代码对我很有帮助。

此外(正如原帖作者已经知道的,但这是为其他人准备的),这里有一个关于如何使用<boost/program_options.hpp>模块的入门Boost教程:https://www.boost.org/doc/libs/1_75_0/doc/html/program_options/tutorial.html.它真的有点“手摇”,他们并不指望你知道这些特定函数到底做了什么。他们想让你知道的只是如何遵循模式和使用库,就像他们在说:

接下来,对storeparse_command_linenotify函数的调用会导致vm包含在命令行上找到的所有选项。

(参考下面示例中的代码):

po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm); 

当你调用add_options()函数时,他们在C++中进行了一些相当高级的黑魔法。

这是他们基本教程示例的完整上下文:

example/first.cpp:

// Copyright Vladimir Prus 2002-2004.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

/* The simplest usage of the library.
 */

#include <boost/program_options.hpp>
namespace po = boost::program_options;

#include <iostream>
#include <iterator>
using namespace std;

int main(int ac, char* av[])
{
    try {

        po::options_description desc("Allowed options");
        desc.add_options()
            ("help", "produce help message")
            ("compression", po::value<double>(), "set compression level")
        ;

        po::variables_map vm;        
        po::store(po::parse_command_line(ac, av, desc), vm);
        po::notify(vm);    

        if (vm.count("help")) {
            cout << desc << "\n";
            return 0;
        }

        if (vm.count("compression")) {
            cout << "Compression level was set to " 
                 << vm["compression"].as<double>() << ".\n";
        } else {
            cout << "Compression level was not set.\n";
        }
    }
    catch(exception& e) {
        cerr << "error: " << e.what() << "\n";
        return 1;
    }
    catch(...) {
        cerr << "Exception of unknown type!\n";
    }

    return 0;
}

这里有更多的示例:https://github.com/boostorg/program_options/tree/develop/example


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接