C++ Boost ProgramOptions超详细讲解

   2023-02-09 学习力0
核心提示:目录一、说明二、示例Boost.ProgramOptions一、说明Boost.ProgramOptionsBoost.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 允许您迭代环境变量。

原文地址:https://yamagota.blog.csdn.net/article/details/128060940
 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • Aurelius vs mORMot vs EntityDAC Delphi 的
    Aurelius vs mORMot vs EntityDAC   Delphi 的 ORM框架:http://www.tmssoftware.com/site/aurelius.asp#product-buy-onlinehttps://synopse.info/fossil/wiki/Synopse+OpenSourcehttps://www.devart.com/entitydac/download.htmlkbmMW  http://www.compo
    02-09
  • 【Ruby】Mac gem的一些坑
    前言自上一次升级MacOS系统后出现jekyll无法构建的问题,当时处理半天。谁知道最近又升级了MacOS,荒废博客多时,今天吝啬写了一篇准备发布,构建报错,问题重新。还是记录下,以防下次升级出问题。问题描述安装jekyll静态博客需要在Ruby环境下运行,于是参照
    02-09
  • iOS oc 调用 swift
    如股票oc要调用swift里面的代码 需要包含固定这个头文件项目名称 LiqunSwiftDemo-Swift.h         #ProjectName#-Swift.h固定的写法swift 目的 是取代oc 但是 不会完全取代 只是前端的替换LiqunSwiftDemo-Swift 点进去 可以看到 所有的swift代码 都产生
    02-09
  • objective-c NSTimer 定时器
    -(void)initTimer{//时间间隔NSTimeInterval timeInterval =3.0 ;//定时器repeats 表示是否需要重复,NO为只重复一次NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(mobileAnimation) userInfo:nil
    02-09
  • Objective-C  日记③ 字符串
    Objective-C 日记③ 字符串
    一、创建字符串、类方法   公式创建NSString  +(id) stringWithFormat:(NSString *) format,……;eg:  NSString *height;  height=[NSString stringWithFormat:@"高度是: %d 长度: %d",10,20];得到的字符串:“高度是: 10 长度: 20” 注意:  省
    02-09
  • Objective-C KVC机制
    Objective-C KVC机制http://blog.csdn.net/omegayy/article/details/7381301全部推翻重写一个版本,这是我在公司内做技术分享的文档总结,对结构、条理做了更清晰的调整。 1.    基本概念MODEL主要是英文文档里面经常出现的一些概念,讲解一下,方便英文
    02-09
  • objective-c 加号 减号 - +
    “加号代表static”是错误的说法,可能跟你那样表达的人其实意思是:“前置加号的方法相当于Java 里面的静态方法”。在Oc中,方法分为类方法和实例方法。前置加号(+)的方法为类方法,这类方法是可以直接用类名来调用的,它的作用主要是创建一个实例。有人把
    02-09
  • Objective-C  日记①
    Objective-C 日记①
    1、Xcode的.m扩展名表示文件含有Object-C代码,C以.c文件,C++以.cpp文件2、头文件声明:C使用:#include,O-C使用#import(当然你也可以使用#include) 3、输出方式:  C:printf("",参数);  O-C:NSLog(@"",参数); 4、布尔类型  C:bool 具有true
    02-09
  • ASP.NET MVC 操作AD 获取域服务器当前用户姓
    #region 根据当前登录域账号 获取AD用户姓名和所在OU目录/// summary/// 根据当前登录域账号 获取AD用户姓名和所在OU目录/// /summary/// param name="searchUser"要搜索的当前用户名/param/// param name="paths"out返回该用户所在OU目录/param/// param nam
    02-09
  • swift和OC - 拆分数组 和 拆分字符串
    1. 拆分数组 /// 根据 数组 截取 指定个数返回 多个数组的集合func splitArray( array: [Date], withSubSize subSize: Int) - [[Date]] {//数组将被拆分成指定长度数组的个数let count = array.count% subSize == 0 ? (array.count/ subSize) : (array.count
    02-08
点击排行