跨平台的命令行参数工具 -- boost program_oprations

1 例子

下例展示了 program_oprations 的基本用法:为程序定义了 3 个参数: help, host, port . 其中 host 与 port 分别定义了简写 h ,p 。host 是必填项, port 是有缺省值(80)的选填项,当 port 有输入时使用输入项,否则使用缺省值 ,当输入了错误的参数时会输出错误信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include "boost/program_options.hpp"

using namespace boost;

int main(int argc, char* argv[])
{
program_options::options_description desc("Allowed options");
desc.add_options()
("help", "help")
("host,h", program_options::value<std::string>()->required(), "host name")
("port,p", program_options::value<int>()->default_value(80), "port number");

program_options::variables_map vm;

try
{
auto ops = program_options::parse_command_line(argc, argv, desc);
boost::program_options::store(ops, vm);
}
catch (boost::program_options::error& e)
{
std::cout << e.what() << std::endl;
return 1;
}

if (vm.count("help"))
{
std::cout << desc << std::endl;
}

if (!vm.count("host"))
{
std::cout << "param is need : " << desc << std::endl;
return 1;
}

//do sth ...

std::cout << "Host is : "
<< vm["host"].as<std::string>()
<< ", Port is : "
<< vm["port"].as<int>()
<< std::endl;

return 0;
}

测试效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>testBoostProgramOptions.exe
param is need : Allowed options:
--help help
-h [ --host ] arg host name
-p [ --port ] arg (=80) port number

>testBoostProgramOptions.exe -h 127.0.0.1
Host is : 127.0.0.1, Port is : 80

>testBoostProgramOptions.exe -h 127.0.0.1 -p 8080
Host is : 127.0.0.1, Port is : 8080

>testBoostProgramOptions.exe -h 127.0.0.1 -p 8080 -o er
unrecognised option '-o'

>testBoostProgramOptions.exe -p 8080
param is need : Allowed options:
--help help
-h [ --host ] arg host name
-p [ --port ] arg (=80) port number

progma_options 库由三部分组成:

  • descrition , 描述组件,用于描述允许的参数及这些参数的值。

  • parser , 解析组件,用于解析输入的参数与值

  • storage , 存储组件, 它将解析器的输出转换成 字符串表示的健与 C++ 类型的值的 map, 并提供了访问这些值的接口。

对于上例来说, program_options 所做的事情,就是使用 options_description 描述了我们期望的参数,使用 parse_command_line 解析了输入参数并使用 store 将参数存储在 variables_map 中。当我们需要使用某个参数时直接从 variables_map 中获取。

2 description 描述组件

描述组件中有三个重要的类 : option_description, value_semanticoptions_description . 前两个类组和使用来描述一个参数, option_description 包含了参数的 名称 ,描述 , 以及一个指向 value_semantic 对象的指针,这个指针对象定义了参数值的类型、可被解析的值、缺省值等信息。 而 options_description 则是 option_description 的容器。

一般地,我们添加一个参数时会先创建一个 option_description 对象,再使用 options_descriptionadd 方法将参数添加到容器中:

1
2
auto descHost = boost::shared_ptr<program_options::option_description>("host", program_options::value<std::string>()->required(), "host name");
desc.add(descHost);

如果参数比较多的话,使用这种方法就比较麻烦了。所以 boost 有特别的添加技巧:

1
2
3
4
5
program_options::options_description desc("Allowed options");
desc.add_options()
("help", "help")
("host,h", program_options::value<std::string>()->required(), "host name")
("port,p", program_options::value<int>()->default_value(80), "port number");

这看起来有点怪怪的,实际上这是对 operator( ) 操作符重载的灵活运用。 options_description.add_options() 返回 options_description_easy_init 类的实例,该类重载了 “( )” 操作符,它模拟了 option_description 的构造函数,接收参数信息构造描述信息对象,添加到描述信息容器中,并返回其对象本身的引用,使其可以再次使用 “( )” 操作符来添加描述信息。

一个 option_description 实例可以分成两个部分: 语法 syntactic语义 semantic 。语法信息规定用户输入参数的形式以及解析器解析的方式,包含参数名称与参数可以使用的符号(token) 的数量, 解析器会使用它将符号组成 (参数名,参数值) 这种形式的键值对,其中参数值是一组字符串( std::vector<std::string> ); 而语义信息则负责将参数值转换成合适的 C++ 类型。

2.1 语法信息

语法信息由 boost::program_options::options_description 类 和 boost::program_options::value_semantic 类的一些方法提供。句法信息包括:

  • 参数名称 用于在程序中做变量标识

  • 参数描述 用于给用户以提示

  • 选项值可用的 token 数 将在解析时使用

如下示例:

1
2
3
4
5
6
options_description desc;
desc.add_options()
("help", "produce help message")
("compression", value<string>(), "compression level")
("verbose", value<string>()->implicit_value("0"), "verbosity level")
("email", value<string>()->multitoken(), "email to send to");

这个示例中, help 不接受任何 token, compression 接收一个 token, verbose 可接受 0 个或 1 个 token, email 可以接受多个 token. 用户可以使用如下命令行:

1
test --help --compression 10 --verbose --email beadle@mars beadle2@mars

2.2 语义信息

语义信息完全由 boost::program_options::value_semantic 类提供。 如下示例:

1
2
3
4
options_description desc;
desc.add_options()
("compression", value<int>()->default_value(10), "compression level")
("email", value< vector<string> >()->composing(), "email");

可以使用如下命令行:

1
test --email a@b.com  --compression 2 --email c@b.com

在此例中, compression 将被解析成 2 , 如果没有输入此参数,那么 compression 将使用缺省值 10 。 而 email 参数则可以由多个值组合而成。

2.3 位置信息

位置参数是一种特殊的参数。如:

1
cat /etc/passwd

这里 /etc/passwd 是没有参数名的。 当然,向前文一样,给它一个参数名也是可以的:

1
cat --input-file=/etc/passwd

但 boost 提供了更优雅的解决方法: positional_options_description 类。该类可以指定:

  • 允许使用多少个位置参数

  • 每个位置参数的名称

  • 每个位置参数的众多参数中的位置

如:

1
2
positional_options_description pd;
pd.add("output-file", 2).add("input-file", -1);

例中允许使用两个位置参数,第一个位置参数是参数中的第2个参数,名称为 “output-file”, 第二个位置参数是参数中的倒数第一个参数,其名称为 “input-file”

3 parsers 解析器

顾名思义,解析器的作用就是将输入的命令行解析成程序可用的 (name,value) 键值对。由前几节可以知道,这个解析器依赖的解析规则是由 描述组件 来确定的。如果不符合描述则会抛出异常。除了通过一般的描述信息来解析,还有以下几种特殊的情况:

  • 参数名称使用了 简写 (或称 =“别名”= )

  • 一个参数名有多个参数值的(如上例中的 email )

  • 命令行中包含了 positional 信息的,见上节内容

常用的解析方法有:

  • program_options::parse_command_line() ,解析命令行

  • program_options::parse_config_file() , 解析配置文件

  • program_options::parse_environment() , 解析环境变量

3.1 Configuration file parser

parse_config_file 方法可以解析类似 INI 风格的配置文件。这种风格以行为单位,一行即为一组参数。当然,还有两种特殊情况:

  • 以括号 [ ] 修饰的行 ,表示一个新的 section

  • # 修饰的注释, 从 # 符号开始到行结束,都为注释

例如:

1
2
3
4
5
host=wandoer.com  #host_name
[user.info] # new section
name=zrking
email=a@b.com
#the end of config_file

这些配置与下面的配置是等价的:

1
2
3
host=wandoer.com
user.info.name=zrking
user.info.email=a@b.com


//待续