程序参数项(program options)是一系列name=value对,program_options 允许程序开发者获得通过命令行(command line)和配置文件(config file)获取这些参数项。
为什么需要这样一个库?为什么比你手工写代码分解命令行参数要好?
在本节,我们从最简单的例子开始,学习program_options库的通常用法。下面的例子仅仅是代码片断,完整例子在“BOOST_ROOT/libs/program_options/example”目录里。对所有例子,假定都在如下名字空间中:
namespace po = boost::program_options;
第一个例子尽可能简单:仅仅包含两个参数项。代码如下(完整代码见“example/first.cpp”):
// Declare the supported options. po::options_description desc("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"; return 1; } 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 Compression level was not set. $bin/gcc/debug/first --help Allowed options: --help : produce help message --compression arg : set compression level $bin/gcc/debug/first --compression 10 Compression level was set to 10.
参数项的值,除了int还有其他类型,还有其他属性,我们下面将讨论。完整例子在“example/options_description.cpp”中。
假如我们写一个编译器程序。它有最优化级别, 包含多个路径, 多个输入文件等参数。描述参数项如下:
int opt; po::options_description desc("Allowed options"); desc.add_options() ("help", "produce help message") ("optimization", po::value(&opt)->default_value(10), "optimization level") ("include-path,I", po::value< vector >(), "include path") ("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
但通常情况常常这么写:
compiler a.cpp
这里要解释一下这种用法。
像上例,没有参数名的命令行选项,在这个库里称为“位置参数项”,也能处理。库能解释“a.cpp”等同于“--input-file=a.cpp”。下面是所需的附加的代码:
po::positional_options_description p; p.add("input-file", -1); po::variables_map vm; 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] Allowed options: --help : produce help message --optimization arg : optimization level -I [ --include-path ] arg : include path --input-file arg : input file $bin/gcc/debug/options_description Optimization level is 10 $bin/gcc/debug/options_description --optimization 4 -I foo a.cpp Include paths are: foo Input files are: a.cpp Optimization level 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_description generic("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_description config("Configuration"); config.add_options() ("optimization", po::value(&opt)->default_value(10), "optimization level") ("include-path,I", po::value< vector >()->composing(), "include path") ; // Hidden options, will be allowed both on command line and // in config file, but will not be shown to the user. po::options_description hidden("Hidden options"); hidden.add_options() ("input-file", po::value< vector >(), "input file") ;
注意在"include-path" 项声明中调用composing 方法,说明从不同来源的值应当被合并在一起,下面很快就会看到。
options_description 类的add 方法能被用于进一步组合参数项:
po::options_description cmdline_options; cmdline_options.add(generic).add(config).add(hidden); po::options_description config_file_options; config_file_options.add(config).add(hidden); po::options_description visible("Allowed options"); visible.add(generic).add(config);
除了额外需要调用 parse_config_file 和 store 函数以外,参数值的解析和存储和通常一样。 但是当命令行和配置文件中同样的参数被指定了如何处理?通常,首选第一个被存储的值。 这说明了“--optimization”项的值如何产生。对“组合(composing)”项,像“include-file”,值被合并在一起。
如何编译的例子如下:
$bin/gcc/debug/multiple_sources Include paths are: /opt Optimization level is 1 $bin/gcc/debug/multiple_sources --help Allows options: Generic options: -v [ --version ] : print version string --help : produce help message Configuration: --optimization n : optimization level -I [ --include-path ] path : include path $bin/gcc/debug/multiple_sources --optimization=4 -I foo a.cpp b.cpp Include paths are: foo /opt Input files are: a.cpp b.cpp Optimization level is 4
为加强理解,这里列出了上面提到的完整源代码。
文件example/first.cpp
#include namespace po = boost::program_options; #include #include using namespace std; int main(int ac, char* av[]) { try { po::options_description desc("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"; return 1; } 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"; return 1; } catch(...) { cerr << "Exception of unknown type!\n"; } return 0; }
文件example/options_description.cpp
#include using namespace boost; namespace po = boost::program_options; #include #include #include using namespace 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, " ")); return os; } int main(int ac, char* av[]) { try { int opt; po::options_description desc("Allowed options"); desc.add_options() ("help", "produce help message") ("optimization", po::value(&opt)->default_value(10), "optimization level") ("include-path,I", po::value< vector >(), "include path") ("input-file", po::value< vector >(), "input file") ; po::positional_options_description p; p.add("input-file", -1); po::variables_map vm; 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; return 0; } 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"; return 1; } return 0; }
文件examples/multiple_sources.cpp
#include namespace po = boost::program_options; #include #include #include using namespace 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, " ")); return os; } int main(int ac, char* av[]) { try { int opt; // Declare a group of options that will be // allowed only on command line po::options_description generic("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_description config("Configuration"); config.add_options() ("optimization", po::value(&opt)->default_value(10), "optimization level") ("include-path,I", po::value< vector >()->composing(), "include path") ; // Hidden options, will be allowed both on command line and // in config file, but will not be shown to the user. po::options_description hidden("Hidden options"); hidden.add_options() ("input-file", po::value< vector >(), "input file") ; po::options_description cmdline_options; cmdline_options.add(generic).add(config).add(hidden); po::options_description config_file_options; config_file_options.add(config).add(hidden); po::options_description visible("Allowed options"); visible.add(generic).add(config); po::positional_options_description p; p.add("input-file", -1); po::variables_map vm; store(po::command_line_parser(ac, av). options(cmdline_options).positional(p).run(), vm); ifstream ifs("multiple_sources.cfg"); store(parse_config_file(ifs, config_file_options), vm); notify(vm); if (vm.count("help")) { cout << visible << "\n"; return 0; } if (vm.count("version")) { cout << "Multiple sources example, version 1.0\n"; return 0; } 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"; return 1; } return 0; }
转载自:http://dozb.bokee.com/1731661.html