C++ Boost ProgramOptions超详细讲解
作者:无水先生
一、说明
Boost.ProgramOptions 是一个可以轻松解析命令行选项的库,例如,控制台应用程序。如果您使用图形用户界面开发应用程序,命令行选项通常并不重要。
要使用 Boost.ProgramOptions 解析命令行选项,需要以下三个步骤:
- 定义命令行选项。您给它们命名并指定哪些可以设置为一个值。如果命令行选项被解析为键/值对,您还可以设置值的类型——例如,它是字符串还是数字。
- 使用解析器评估命令行。您可以从 main() 的两个参数获取命令行,这两个参数通常称为 argc 和 argv。
- 存储解析器评估的命令行选项。 Boost.ProgramOptions 提供了一个派生自 std::map 的类,它将命令行选项保存为名称/值对。之后,您可以检查存储了哪些选项以及它们的值是什么。
二、示例Boost.ProgramOptions
示例 63.1。展示了使用 Boost.ProgramOptions 解析命令行选项的基本方法。
示例 63.1。 Boost.ProgramOptions 的基本方法
#include <boost/program_options.hpp> #include <iostream> using namespace boost::program_options; void on_age(int age) { std::cout << "On age: " << age << '\n'; } int main(int argc, const char *argv[]) { try { options_description desc{"Options"}; desc.add_options() ("help,h", "Help screen") ("pi", value<float>()->default_value(3.14f), "Pi") ("age", value<int>()->notifier(on_age), "Age"); variables_map vm; store(parse_command_line(argc, argv, desc), vm); notify(vm); if (vm.count("help")) std::cout << desc << '\n'; else if (vm.count("age")) std::cout << "Age: " << vm["age"].as<int>() << '\n'; else if (vm.count("pi")) std::cout << "Pi: " << vm["pi"].as<float>() << '\n'; } catch (const error &ex) { std::cerr << ex.what() << '\n'; } }
要使用 Boost.ProgramOptions,请包含头文件 boost/program_options.hpp。您可以在命名空间 boost::program_options 中访问此库中的所有类和函数。
使用类 boost::program_options::options_description 来描述命令行选项。这种类型的对象可以写入诸如 std::cout 之类的流,以显示可用命令行选项的概览。传递给构造函数的字符串为概览提供了一个名称,作为命令行选项的标题。
boost::program_options::options_description 定义了一个成员函数 add() ,它需要一个 boost::program_options::option_description 类型的参数。您调用此函数来描述每个命令行选项。示例 63.1 不是为每个命令行选项调用此函数,而是调用成员函数 add_options(),这使得该任务更容易。
add_options() 返回一个代表 boost::program_options::options_description 类型对象的代理对象。代理对象的类型无关紧要。更有趣的是代理对象简化了许多命令行选项的定义。它使用重载运算符 operator(),您可以调用它来传递所需的数据以定义命令行选项。此运算符返回对同一代理对象的引用,这允许您多次调用 operator()。
示例 63.1 在代理对象的帮助下定义了三个命令行选项。第一个命令行选项是 --help。此选项的说明设置为“帮助屏幕”。该选项是一个开关,而不是名称/值对。您可以在命令行上设置 --help 或忽略它。无法将 --help 设置为一个值。
请注意,传递给 operator() 的第一个字符串是“help,h”。您可以为命令行选项指定简称。短名称必须仅由一个字母组成,并设置在逗号之后。现在可以使用 --help 或 -h 显示帮助。
除了 --help 之外,还定义了另外两个命令行选项:--pi 和 --age。这些选项不是开关,它们是名称/值对。 --pi 和 --age 都应设置为一个值。
您将指向类型为 boost::program_options::value_semantic 的对象的指针作为第二个参数传递给 operator() 以将选项定义为名称/值对。您不需要直接访问 boost::program_options::value_semantic。您可以使用辅助函数 boost::program_options::value(),它创建一个类型为 boost::program_options::value_semantic 的对象。 boost::program_options::value() 返回对象的地址,然后您可以使用 operator() 将其传递给代理对象。
boost::program_options::value() 是一个函数模板,它将命令行选项值的类型作为模板参数。因此,命令行选项 --age 需要一个整数,而 --pi 需要一个浮点数。
从 boost::program_options::value() 返回的对象提供了一些有用的成员函数。例如,您可以调用 default_value() 来提供默认值。如果未在命令行中使用该选项,则示例 63.1 将 --pi 设置为 3.14。
notifier() 将函数链接到命令行选项的值。然后使用命令行选项的值调用该函数。在示例 63.1 中,函数 on_age() 链接到 --age。如果命令行选项 --age 用于设置年龄,则年龄将传递给 on_age() 并将其写入标准输出。
使用 on_age() 等函数处理值是可选的。您不必使用 notifier(),因为可以通过其他方式访问值。
定义所有命令行选项后,您可以使用解析器。在示例 63.1 中,辅助函数 boost::program_options::parse_command_line() 被调用来解析命令行。此函数采用定义命令行的 argc 和 argv,以及包含选项说明的 desc。 boost::program_options::parse_command_line() 在 boost::program_options::parsed_options 类型的对象中返回解析后的选项。你通常不直接访问这个对象。相反,您将它传递给 boost::program_options::store(),它将解析的选项存储在容器中。
示例 63.1 将 vm 作为第二个参数传递给 boost::program_options::store()。 vm 是 boost::program_options::variables_map 类型的对象。此类派生自类 std::map<std::string, boost::program_options::variable_value>,因此提供与 std::map 相同的成员函数。例如,您可以调用 count() 来检查某个命令行选项是否已被使用并存储在容器中。
在示例 63.1 中,在访问 vm 和调用 count() 之前,调用了 boost::program_options::notify()。此函数触发诸如 on_age() 之类的函数,这些函数使用 notifier() 链接到一个值。如果没有 boost::program_options::notify(),将不会调用 on_age()。
vm 可以让您检查某个命令行选项是否存在,还可以让您访问命令行选项设置的值。该值的类型是 boost::program_options::variable_value,一个在内部使用 boost::any 的类。您可以从成员函数 value() 中获取类型为 boost::any 的对象。
示例 63.1 调用 as(),而不是 value()。此成员函数将命令行选项的值转换为作为模板参数传递的类型。 as() 使用 boost::any_cast() 进行类型转换。
确保您传递给 as() 的类型与命令行选项的类型相匹配。例如,示例 63.1 期望将命令行选项 --age 设置为 int 类型的数字,因此必须将 int 作为模板参数传递给 as()。
您可以通过多种方式启动示例 63.1。这是一个例子:
测试
在这种情况下,显示 Pi: 3.14。因为 --pi 未在命令行上设置,所以显示默认值。
此示例使用 --pi 设置一个值:
测试 --pi 3.1415
该程序现在显示 Pi:3.1415。
这个例子也传递了一个年龄:
测试 --pi 3.1415 --age 29
输出现在是 On age: 29 和 Age: 29。第一行是在调用 boost::program_options::notify() 时写入的;这会触发 on_age() 的执行。 --pi 没有输出,因为程序使用 else if 语句,如果未设置 --age 则仅显示用 --pi 设置的值。
此示例显示帮助:
测试 -h
您可以获得所有命令行选项的完整概述:
Options: -h [ --help ] Help screen --pi arg (=3.1400001) Pi --age arg Age
如您所见,帮助可以以两种不同的方式显示,因为为该命令行选项定义了一个短名称。对于 --pi,显示默认值。命令行选项及其描述会自动格式化。您只需将类型为 boost::program_options::options_description 的对象写入标准输出,如示例 63.1 所示。
现在,像这样开始这个例子:
测试——年龄
输出如下:
缺少选项“--age”所需的参数。
因为未设置 --age,boost::program_options::parse_command_line() 中使用的解析器会抛出 boost::program_options::error 类型的异常。捕获异常,并将错误消息写入标准输出。
boost::program_options::error 派生自 std::logic_error。 Boost.ProgramOptions 定义了额外的异常,它们都派生自 boost::program_options::error。其中一个异常是 boost::program_options::invalid_syntax,如果您没有为 --age 提供值,它就是在示例 63.1 中抛出的异常。
示例 63.2 引入了更多可用于 Boost.ProgramOptions 的配置设置。
示例 63.2。使用 Boost.ProgramOptions 的特殊配置设置
#include <boost/program_options.hpp> #include <string> #include <vector> #include <algorithm> #include <iterator> #include <iostream> using namespace boost::program_options; void to_cout(const std::vector<std::string> &v) { std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>{ std::cout, "\n"}); } int main(int argc, const char *argv[]) { try { int age; options_description desc{"Options"}; desc.add_options() ("help,h", "Help screen") ("pi", value<float>()->implicit_value(3.14f), "Pi") ("age", value<int>(&age), "Age") ("phone", value<std::vector<std::string>>()->multitoken()-> zero_tokens()->composing(), "Phone") ("unreg", "Unrecognized options"); command_line_parser parser{argc, argv}; parser.options(desc).allow_unregistered().style( command_line_style::default_style | command_line_style::allow_slash_for_short); parsed_options parsed_options = parser.run(); variables_map vm; store(parsed_options, vm); notify(vm); if (vm.count("help")) std::cout << desc << '\n'; else if (vm.count("age")) std::cout << "Age: " << age << '\n'; else if (vm.count("phone")) to_cout(vm["phone"].as<std::vector<std::string>>()); else if (vm.count("unreg")) to_cout(collect_unrecognized(parsed_options.options, exclude_positional)); else if (vm.count("pi")) std::cout << "Pi: " << vm["pi"].as<float>() << '\n'; } catch (const error &ex) { std::cerr << ex.what() << '\n'; } }
设置配置后,在解析器上调用 run()。此成员函数在 boost::program_options::parsed_options 类型的对象中返回解析的命令行选项,您可以将其传递给 boost::program_options::store() 以将选项存储在 vm 中。
在代码的后面,示例 63.2 再次访问 vm 以评估命令行选项。只有对 boost::program_options::collect_unrecognized() 的调用是新的。此函数为命令行选项 --unreg 调用。该函数需要一个 boost::program_options::parsed_options 类型的对象,它由 run() 返回。它在 std::vector<std::string> 中返回所有未知的命令行选项。例如,如果您使用 test --unreg --abc 启动程序,--abc 将写入标准输出。
当 boost::program_options::exclude_positional 作为第二个参数传递给 boost::program_options::collect_unrecognized() 时,位置选项将被忽略。对于示例 63.2,这无关紧要,因为没有定义位置选项。但是,boost::program_options::collect_unrecognized() 需要此参数。
示例 63.3 说明了位置选项。
示例 63.3。 Boost.ProgramOptions 的位置选项
#include <boost/program_options.hpp> #include <string> #include <vector> #include <algorithm> #include <iterator> #include <iostream> using namespace boost::program_options; void to_cout(const std::vector<std::string> &v) { std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>{std::cout, "\n"}); } int main(int argc, const char *argv[]) { try { options_description desc{"Options"}; desc.add_options() ("help,h", "Help screen") ("phone", value<std::vector<std::string>>()-> multitoken()->zero_tokens()->composing(), "Phone"); positional_options_description pos_desc; pos_desc.add("phone", -1); command_line_parser parser{argc, argv}; parser.options(desc).positional(pos_desc).allow_unregistered(); parsed_options parsed_options = parser.run(); variables_map vm; store(parsed_options, vm); notify(vm); if (vm.count("help")) std::cout << desc << '\n'; else if (vm.count("phone")) to_cout(vm["phone"].as<std::vector<std::string>>()); } catch (const error &ex) { std::cerr << ex.what() << '\n'; } }
示例 63.3 使用类 boost::program_options::positional_options_description 将 --phone 定义为位置选项。此类提供成员函数 add(),它需要传递命令行选项的名称和位置。该示例传递“phone”和 -1。
使用位置选项,可以在命令行上设置值,而无需使用命令行选项。您可以像这样启动示例 63.3:
测试 123 456
即使未使用 --phone,123 和 456 也会被识别为电话号码。
在类型为 boost::program_options::positional_options_description 的对象上调用 add() 会将命令行上的值分配给使用位置编号的命令行选项。当使用命令行测试 123 456 调用示例 63.3 时,123 的位置编号为 0,456 的位置编号为 1。示例 63.3 将 -1 传递给 add(),它将所有值 - 123 和 456 - 分配给 - -电话。如果您更改示例 63.3 以将值 0 传递给 add(),则只有 123 会被识别为电话号码。如果将 1 传递给 add(),则只会识别 456。
pos_desc 与 positional() 一起传递给解析器。这就是解析器如何知道哪些命令行选项是位置的。
请注意,您必须确保定义了位置选项。例如,在示例 63.3 中,“phone”只能传递给 add(),因为 --phone 的定义已经存在于 desc 中。
在之前的所有示例中,Boost.ProgramOptions 用于解析命令行选项。但是,该库也支持从文件加载配置选项。如果必须重复设置相同的命令行选项,这会很有用。
示例 63.4。从配置文件加载选项
#include <boost/program_options.hpp> #include <string> #include <fstream> #include <iostream> using namespace boost::program_options; int main(int argc, const char *argv[]) { try { options_description generalOptions{"General"}; generalOptions.add_options() ("help,h", "Help screen") ("config", value<std::string>(), "Config file"); options_description fileOptions{"File"}; fileOptions.add_options() ("age", value<int>(), "Age"); variables_map vm; store(parse_command_line(argc, argv, generalOptions), vm); if (vm.count("config")) { std::ifstream ifs{vm["config"].as<std::string>().c_str()}; if (ifs) store(parse_config_file(ifs, fileOptions), vm); } notify(vm); if (vm.count("help")) std::cout << generalOptions << '\n'; else if (vm.count("age")) std::cout << "Your age is: " << vm["age"].as<int>() << '\n'; } catch (const error &ex) { std::cerr << ex.what() << '\n'; } }
示例 63.4 使用了两个类型为 boost::program_options::options_description 的对象。 generalOptions 定义必须在命令行上设置的选项。 fileOptions 定义可以从配置文件加载的选项。
不必使用类型为 boost::program_options::options_description 的两个不同对象来定义选项。如果命令行和文件的选项集相同,则可以只使用一个。在示例 63.4 中,分隔选项是有意义的,因为您不想允许在配置文件中设置 --help。如果允许并且用户将该选项放入配置文件中,则程序每次都会显示帮助屏幕。
示例 63.4 从配置文件加载 --age。您可以将配置文件的名称作为命令行选项传递。出于这个原因,在此示例中,--config 是在 generalOptions 中定义的。
在使用 boost::program_options::parse_command_line() 解析命令行选项并存储在 vm 中后,该示例检查是否设置了 --config。如果是,则使用 std::ifstream 打开配置文件。 std::ifstream 对象与描述选项的文件选项一起传递给函数 boost::program_options::parse_config_file()。 boost::program_options::parse_config_file() 做与 boost::program_options::parse_command_line() 相同的事情,并在 boost::program_options::parsed_options 类型的对象中返回解析后的选项。该对象被传递给 boost::program_options::store() 以将解析的选项存储在 vm 中。
如果您创建一个名为 config.txt 的文件,将 age=29 放入该文件,然后执行下面的命令行,您将得到显示的结果。
测试 --config config.txt
输出如下:
Your age is: 29
如果您在命令行和配置文件中支持相同的选项,您的程序可能会解析相同的选项两次——一次使用 boost::program_options::parse_command_line(),一次使用 boost::program_options::parse_config_file()。函数调用的顺序决定了您将在 vm 中找到哪个值。一旦命令行选项的值存储在 vm 中,该值将不会被覆盖。该值是由命令行上的选项还是在配置文件中设置的,仅取决于您调用 store() 函数的顺序。
Boost.ProgramOptions 还定义了函数 boost::program_options::parse_environment(),可用于从环境变量加载选项。类 boost::environment_iterator 允许您迭代环境变量。
到此这篇关于C++ Boost ProgramOptions超详细讲解的文章就介绍到这了,更多相关C++ Boost ProgramOptions内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!