[C++Boost]程序參數項解析庫Program_options使用指南
介紹
程序參數項(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; }RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成