C++ Boost ProgramOptions超详细讲解

目录
  • 一、说明
  • 二、示例Boost.ProgramOptions

一、说明

Boost.ProgramOptions

Boost.ProgramOptions 是一个可以轻松解析命令行选项的库,例如,控制台应用程序。如果您使用图形用户界面开发应用程序,命令行选项通常并不重要。

要使用 Boost.ProgramOptions 解析命令行选项,需要以下三个步骤:

  • 定义命令行选项。您给它们命名并指定哪些可以设置为一个值。如果命令行选项被解析为键/值对,您还可以设置值的类型——例如,它是字符串还是数字。
  • 使用解析器评估命令行。您可以从 main() 的两个参数获取命令行,这两个参数通常称为 argc 和 argv。
  • 存储解析器评估的命令行选项。 Boost.ProgramOptions 提供了一个派生自 std::map 的类,它将命令行选项保存为名称/值对。之后,您可以检查存储了哪些选项以及它们的值是什么。

二、示例Boost.ProgramOptions

Example 63.1

示例 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()。

Example 63.1

示例 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';
  }
}

Example 63.2

设置配置后,在解析器上调用 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';
  }
}

Example 63.3

示例 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';
  }
}

Example 63.4

示例 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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++ Boost MetaStateMachine定义状态机超详细讲解

    目录 一.说明 二.示例和代码 一.说明 Boost.MetaStateMachine 用于定义状态机.状态机通过对象的状态来描述对象.它们描述了存在哪些状态以及状态之间可能存在哪些转换. Boost.MetaStateMachine 提供了三种不同的方式来定义状态机.创建状态机所需编写的代码取决于前端. 如果使用基本前端或函数前端,则可以用常规方式定义状态机:创建类,从 Boost.MetaStateMachine 提供的其他类派生它们,定义所需的成员变量,并编写所需的 C++自己编码.基本前

  • C++ Boost Uuid超详细讲解

    目录 一.说明 二.Boost.Uuid库示例和代码 一.说明 Boost.Uuid 为 UUID 提供生成器. UUID 是不依赖于中央协调实例的通用唯一标识符.例如,没有数据库存储所有生成的 UUID,可以检查这些 UUID 是否使用了新的 UUID. UUID 由必须唯一标识组件的分布式系统使用.例如,Microsoft 使用 UUID 来识别 COM 世界中的接口.对于为 COM 开发的新接口,可以轻松分配唯一标识符. UUID 是 128 位数字.存在多种生成 UUID 的方法.例如,

  • C++ Boost Assign超详细讲解

    目录 说明 Exercise 说明 Boost.Assign Boost.Assign 库提供了帮助函数来初始化容器或向容器添加元素.如果需要将许多元素存储在一个容器中,这些函数尤其有用.多亏了 Boost.Assign 提供的函数,您不需要重复调​​用像 push_back() 这样的成员函数来将元素一个一个地插入到容器中. 如果您使用支持 C++11 的开发环境,则可以从初始化列表中获益.通常您可以将任意数量的值传递给构造函数来初始化容器.多亏了初始化列表,你不必依赖 C++11 的 Boo

  • C++ Boost Archive超详细讲解

    目录 一.说明 二.关于Archive库 一.说明 对Boost.Serialization库的应用,存在如下内容: Archive Pointers and References Serialization of Class Hierarchy Objects Wrapper Functions for Optimization Boost.Serialization 库可以将 C++ 程序中的对象转换为可以保存和加载以恢复对象的字节序列.有不同的数据格式可用于定义生成字节序列的规则. Boo

  • C++ Boost.Signals2信号/槽概念

    目录 一.关于Boost.Signals2 二.关于Signals库 练习 一.关于Boost.Signals2 Boost.Signals2 实现了信号/槽的概念.一个或多个函数(称为槽)与可以发出信号的对象相关联.每次发出信号时,都会调用链接的函数. 信号/槽概念在开发具有图形用户界面的应用程序时非常有用.可以对按钮进行建模,以便在用户单击它们时发出信号.它们可以支持指向许多函数的链接以处理用户输入.这样就可以灵活地处理事件. std::function 也可用于事件处理. std::fun

  • C++ Boost Utility超详细讲解

    目录 一.说明 二.Boost.Utility库示例和代码 一.说明 Boost.Utility 库是杂项.有用的类和函数的集合,它们太小而无法在独立库中维护.虽然实用程序很小并且可以快速学习,但它们完全无关.与其他章节中的示例不同,此处的代码示例不是相互构建的,因为它们是独立的实用程序. 虽然大多数实用程序都在 boost/utility.hpp 中定义,但有些实用程序有自己的头文件.以下示例包括所介绍的实用程序的相应头文件. 二.Boost.Utility库示例和代码 示例 69.1.使用

  • C++ Boost Flyweight库使用介绍

    目录 一.说明 二.库Boost.Flyweight 炼习 一.说明 以下库用于设计模式. Boost.Flyweight 有助于在程序中使用许多相同的对象并且需要减少内存消耗的情况. Boost.Signals2 使得使用观察者设计模式变得容易.这个库被称为 Boost.Signals2 因为它实现了信号/槽的概念. Boost.MetaStateMachine 使得将状态机从 UML 转移到 C++ 成为可能. 本节内容 66. Boost.Flyweight 67. Boost.Signa

  • C++ Boost ProgramOptions超详细讲解

    目录 一.说明 二.示例Boost.ProgramOptions 一.说明 Boost.ProgramOptions Boost.ProgramOptions 是一个可以轻松解析命令行选项的库,例如,控制台应用程序.如果您使用图形用户界面开发应用程序,命令行选项通常并不重要. 要使用 Boost.ProgramOptions 解析命令行选项,需要以下三个步骤: 定义命令行选项.您给它们命名并指定哪些可以设置为一个值.如果命令行选项被解析为键/值对,您还可以设置值的类型——例如,它是字符串还是数字

  • C++ Boost Lockfree超详细讲解使用方法

    目录 一.说明 二.示例和代码 Boost.Lockfree 一.说明 Boost.Lockfree 提供线程安全和无锁容器.可以从多个线程访问此库中的容器,而无需同步访问. 在 1.56.0 版本中,Boost.Lockfree 只提供了两个容器:boost::lockfree::queue 类型的队列和 boost::lockfree::stack 类型的栈.对于队列,可以使用第二个实现:boost::lockfree::spsc_queue.此类针对只有一个线程写入队列和只有一个线程从队列

  • C++ Boost ScopeExit超详细讲解

    目录 一.提要 二.退出作用域(Boost.ScopeExit) 2.1 范例1.UsingBOOST_SCOPE_EXIT 2.2 示例2.Boost.ScopeExit和C++11的lambda函数 2.3 示例3.特点BOOST_SCOPE_EXIT 三.练习 一.提要 资源有很多种,每种都封装一套,还是挺繁琐的!对于比较少使用或者一个程序很可能只会用一次的资源,我们不想封装,在这种情况下用Boost.ScopeExit. 二.退出作用域(Boost.ScopeExit) 库 Boost.

  • C++ Boost System超详细讲解

    目录 一.说明 二.关于 Boost.System库 一.说明 以下库支持错误处理. Boost.System 提供类来描述和识别错误.自 C++11 以来,这些类已成为标准库的一部分. Boost.Exception 使得在抛出异常后附加数据成为可能. 二.关于 Boost.System库 Boost.System Boost.System 是一个库,本质上定义了四个类来识别错误.所有四个类都已添加到 C++11 的标准库中.如果您的开发环境支持 C++11,则无需使用 Boost.Syste

  • C++ Boost Format超详细讲解

    Boost.Format Boost.Format 提供了函数 std::printf() 的替代品. std::printf() 源自 C 标准并允许格式化数据输出.但是,它既不是类型安全的,也不是可扩展的. Boost.Format 提供了一种类型安全且可扩展的替代方案. Boost.Format 提供了一个名为 boost::format 的类,该类在 boost/format.hpp 中定义.与 std::printf() 类似,将包含用于控制格式的特殊字符的字符串传递给 boost::

  • C++ Boost Exception超详细讲解

    Boost.Exception 库提供了一种新的异常类型 boost::exception,它允许您在抛出异常后将数据添加到异常中.此类型在 boost/exception/exception.hpp 中定义.由于 Boost.Exception 将其类和函数分布在多个头文件中,以下示例访问主头文件 boost/exception/all.hpp 以避免一个接一个地包含头文件. Boost.Exception 支持 C++11 标准的机制,该机制将异常从一个线程传输到另一个线程. boost::

随机推荐