程序参数项(programoptions)是一系列name=value对,program_options允许程序开发者获得通过命令行(commandline)和配置文件(configfile)获取这些参数项。
为什么需要这样一个库?为什么比你手工写代码分解命令行参数要好?
使用更容易。定义参数处理的语法简单,库自身很小。像转换参数值到指定的类型和保存参数值到变量的事情都是自动处理。
错误报告更友好。可报告错误的命令行参数。另外这个库能自动生成使用帮助,避免手工更新使用帮助导致的不一致。
参数能从不同地方读取。当命令行参数不能满足要求,需要改用配置文件或环境变量。
这些功能都能支持,代码改动很小。使用指南
快速入门
使用详解
参数多种来源
在本节,我们从最简单的例子开始,学习program_options库的通常用法。下面的例子仅仅是代码片断,完整例子在“BOOST_ROOT/libs/program_options/example”目录里。对所有例子,假定都在如下名字空间中:
namespacepo = boost::program_options;
第一个例子尽可能简单:仅仅包含两个参数项。代码如下(完整代码见“example/first.cpp”):
//Declare the supported options.
po::options_descriptiondesc("Allowed options");
desc.add_options()
("help","produce help message")
("compression",po::value(), "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";
return1;
}
if(vm.count("compression")) {
cout<< "Compression level was set to "
<<vm["compression"].as() << ".\n";
}else {
cout<< "Compression level was not set.\n";
}
首先用类options_description描述所有允许的参数项,类的add_options方法返回定义了operator()的代理对象,调用其operator()用来实际描述参数项,函数参数是参数项名称,相关值信息,参数项描述。本例中,第一个参数项没有值,第二个参数项有一个int类型的值。
其后,定义一个类variables_map对象。用来存储参数项的值,其能存储任意类型的值。接着调用store,parse_command_line和notify函数,解析命令行参数并保存到vm中。
现在,可以像使用std::map一样来使用variables_map类,但存储的值必须能通过at方法找回。假如调用as方法指定的类型和实际类型不符,将抛出异常)
现在自己可以尝试编译一下代码,如何编译的例子如下:
$bin/gcc/debug/first
Compressionlevel was not set.
$bin/gcc/debug/first--help
Allowedoptions:
--help : produce help message
--compressionarg : set compression level
$bin/gcc/debug/first--compression 10
Compressionlevel was set to 10.
参数项的值,除了int还有其他类型,还有其他属性,我们下面将讨论。完整例子在“example/options_description.cpp”中。
假如我们写一个编译器程序。它有最优化级别,包含多个路径,多个输入文件等参数。描述参数项如下:
intopt;
po::options_descriptiondesc("Allowed options");
desc.add_options()
("help","produce help message")
("optimization",po::value(&opt)->default_value(10),
"optimizationlevel")
("include-path,I",po::value< vector >(),
"includepath")
("input-file",po::value< vector >(), "input file")
;
The"--help"项和前例一样,在项目中有这个参数项是个好注意。
The"optimization"项体现两个新特性.首先,我们传递变量(&opt)地址,这个变量用来保存获得的参数项的值。然后,指定一个缺省值,用在此参数项用户没有设置值的时候。
The"include-path"项说明了options_description类接口仅仅来源于命令行的例子。用户喜欢用短参数项名称,“include-path,I”名指出短参数项名是“I”.因此,“--include-path”和“-I”都能用。
The"input-file"参数项指定处理文件列表。像下面这样写没有问题:
compiler--input-file=a.cpp
但通常情况常常这么写:
compilera.cpp
这里要解释一下这种用法。
像上例,没有参数名的命令行选项,在这个库里称为“位置参数项”,也能处理。库能解释“a.cpp”等同于“--input-file=a.cpp”。下面是所需的附加的代码:
po::positional_options_descriptionp;
p.add("input-file",-1);
po::variables_mapvm;
po::store(po::command_line_parser(ac,av).
options(desc).positional(p).run(),vm);
po::notify(vm);
前两行指出所有的“位置参数项”应被翻译成“input-file”项。要注意用command_line_parser类解析命令行,而不是parse_command_line函数。parse_command_line函数是为处理简单情况对command_line_parser类的封装,但现在要传递附加信息就
不适用了。
现在,所有参数项被描述且被解析。我们暂不实现剩下的编译逻辑,仅仅打印参数项:
if(vm.count("include-path"))
{
cout<< "Include paths are: "
<<vm["include-path"].as< vector >() << "\n";
}
if(vm.count("input-file"))
{
cout<< "Input files are: "
<<vm["input-file"].as< vector >() << "\n";
}
cout<< "Optimization level is " << opt <<"\n";
如何编译的例子如下:
$bin/gcc/debug/options_description--help
Usage:options_description [options]
Allowedoptions:
--help : produce help message
--optimizationarg : optimization level
-I[ --include-path ] arg : include path
--input-filearg : input file
$bin/gcc/debug/options_description
Optimizationlevel is 10
$bin/gcc/debug/options_description--optimization 4 -I foo a.cpp
Includepaths are: foo
Inputfiles are: a.cpp
Optimizationlevel is 4
这里有个小问题,“帮助信息”要求指定“--input-file”项名称,这将把用户弄糊涂。在后面的例子中可看到怎样隐藏这个信息。
要求用户在命令行给我们的编译器指定所有参数不太现实。假如用户安装新库且想传递一个附加命令行参数该怎么做?想实现一次选择多次运行的时候使用该怎么做?可以创建一个配置文件把命令行参数组织在一起来完成这个工作。
当然,解析时需要结合命令行参数和配置文件两方面的值。例如,在命令行指定的“最优化级别”的值将覆盖配置文件的值。另一方面,“包含路径”应该包含命令行和配置文件里两方面的值。
下面看代码。完整代码在“examples/multiple_sources.cpp”。参数项定义有两种细节。首先,定义options_description类的几个实例。原因是,通常情况下,不是所有参数项属性是类似的。一些项,例如上面的“input-file”,应该在自动帮助信息里不出现。一下项仅在配置文件中有意义。其次,帮助信息有组织的输出是个好主意,而不仅仅是参数项的长列表。下面声明几组参数项:
//Declare a group of options that will be
//allowed only on command line
po::options_descriptiongeneric("Generic options");
generic.add_options()
("version,v","print version string")
("help","produce help message")
;
//Declare a group of options that will be
//allowed both on command line and in
//config file
po::options_descriptionconfig("Configuration");
config.add_options()
("optimization",po::value(&opt)->default_value(10),
"optimizationlevel")
("include-path,I",
po::value<vector >()->composing(),
"includepath")
;
//Hidden options, will be allowed both on command line and
//in config file, but will not be shown to the user.
po::options_descriptionhidden("Hidden options");
hidden.add_options()
("input-file",po::value< vector >(), "input file")
;
注意在"include-path"项声明中调用composing方法,说明从不同来源的值应当被合并在一起,下面很快就会看到。
options_description类的add方法能被用于进一步组合参数项:
po::options_descriptioncmdline_options;
cmdline_options.add(generic).add(config).add(hidden);
po::options_descriptionconfig_file_options;
config_file_options.add(config).add(hidden);
po::options_descriptionvisible("Allowed options");
visible.add(generic).add(config);
除了额外需要调用parse_config_file和store函数以外,参数值的解析和存储和通常一样。但是当命令行和配置文件中同样的参数被指定了如何处理?通常,首选第一个被存储的值。这说明了“--optimization”项的值如何产生。对“组合(composing)”项,像“include-file”,值被合并在一起。
如何编译的例子如下:
$bin/gcc/debug/multiple_sources
Includepaths are: /opt
Optimizationlevel is 1
$bin/gcc/debug/multiple_sources--help
Allowsoptions:
Genericoptions:
-v[ --version ] : print version string
--help : produce help message
Configuration:
--optimizationn : optimization level
-I[ --include-path ] path : include path
$bin/gcc/debug/multiple_sources--optimization=4 -I foo a.cpp b.cpp
Includepaths are: foo /opt
Inputfiles are: a.cpp b.cpp
Optimizationlevel is
为加强理解,这里列出了上面提到的完整源代码。
文件example/first.cpp
#include
namespacepo = boost::program_options;
#include
#include
usingnamespace std;
intmain(int ac, char* av[])
{
try{
po::options_descriptiondesc("Allowed options");
desc.add_options()
("help","produce help message")
("compression",po::value(), "set compression level")
;
po::variables_mapvm;
po::store(po::parse_command_line(ac,av, desc), vm);
po::notify(vm);
if(vm.count("help")) {
cout<< desc << "\n";
return1;
}
if(vm.count("compression")) {
cout<< "Compression level was set to "
<<vm["compression"].as() << ".\n";
}else {
cout<< "Compression level was not set.\n";
}
}
catch(exception&e) {
cerr<< "error: " << e.what() << "\n";
return1;
}
catch(...){
cerr<< "Exception of unknown type!\n";
}
return0;
}
文件example/options_description.cpp
#include
usingnamespace boost;
namespacepo = boost::program_options;
#include
#include
#include
usingnamespace std;
//A helper function to simplify the main part.
template
ostream&operator<<(ostream& os, const vector& v)
{
copy(v.begin(),v.end(), ostream_iterator(cout, " "));
returnos;
}
intmain(int ac, char* av[])
{
try{
intopt;
po::options_descriptiondesc("Allowed options");
desc.add_options()
("help","produce help message")
("optimization",po::value(&opt)->default_value(10),
"optimizationlevel")
("include-path,I",po::value< vector >(),
"includepath")
("input-file",po::value< vector >(), "input file")
;
po::positional_options_descriptionp;
p.add("input-file",-1);
po::variables_mapvm;
po::store(po::command_line_parser(ac,av).
options(desc).positional(p).run(),vm);
po::notify(vm);
if(vm.count("help")) {
cout<< "Usage: options_description [options]\n";
cout<< desc;
return0;
}
if(vm.count("include-path"))
{
cout<< "Include paths are: "
<<vm["include-path"].as< vector >() << "\n";
}
if(vm.count("input-file"))
{
cout<< "Input files are: "
<<vm["input-file"].as< vector >() << "\n";
}
cout<< "Optimization level is " << opt <<"\n";
}
catch(exception&e)
{
cout<< e.what() << "\n";
return1;
}
return0;
}
文件examples/multiple_sources.cpp
#include
namespacepo = boost::program_options;
#include
#include
#include
usingnamespace std;
//A helper function to simplify the main part.
template
ostream&operator<<(ostream& os, const vector& v)
{
copy(v.begin(),v.end(), ostream_iterator(cout, " "));
returnos;
}
intmain(int ac, char* av[])
{
try{
intopt;
//Declare a group of options that will be
//allowed only on command line
po::options_descriptiongeneric("Generic options");
generic.add_options()
("version,v","print version string")
("help","produce help message")
;
//Declare a group of options that will be
//allowed both on command line and in
//config file
po::options_descriptionconfig("Configuration");
config.add_options()
("optimization",po::value(&opt)->default_value(10),
"optimizationlevel")
("include-path,I",
po::value<vector >()->composing(),
"includepath")
;
//Hidden options, will be allowed both on command line and
//in config file, but will not be shown to the user.
po::options_descriptionhidden("Hidden options");
hidden.add_options()
("input-file",po::value< vector >(), "input file")
;
po::options_descriptioncmdline_options;
cmdline_options.add(generic).add(config).add(hidden);
po::options_descriptionconfig_file_options;
config_file_options.add(config).add(hidden);
po::options_descriptionvisible("Allowed options");
visible.add(generic).add(config);
po::positional_options_descriptionp;
p.add("input-file",-1);
po::variables_mapvm;
store(po::command_line_parser(ac,av).
options(cmdline_options).positional(p).run(),vm);
ifstreamifs("multiple_sources.cfg");
store(parse_config_file(ifs,config_file_options), vm);
notify(vm);
if(vm.count("help")) {
cout<< visible << "\n";
return0;
}
if(vm.count("version")) {
cout<< "Multiple sources example, version 1.0\n";
return0;
}
if(vm.count("include-path"))
{
cout<< "Include paths are: "
<<vm["include-path"].as< vector >() << "\n";
}
if(vm.count("input-file"))
{
cout<< "Input files are: "
<<vm["input-file"].as< vector >() << "\n";
}
cout<< "Optimization level is " << opt <<"\n";
}
catch(exception&e)
{
cout<< e.what() << "\n";
return1;
}
return0;
}