Vocabulary
- Program options, options
- pair. (name, value)
- Token
- Position options
- Composing options
Option Details
沒錯,你得到它了!option value 不僅只侷限於 int 型別,此外還可以有其它下面的屬性(proerties)。完整程式可以在
example/options_description.cpp
找到。
想像你在寫一個 compiler ,你可以設定它的 options:
- Otimization level
- Include paths
- Input files
- 其它做些有趣的事。
下面的程式碼試著描述這些 options :
int opt; po::options_description desc("Allowed options"); desc.add_options() ("help", "produce help message") ("optimization", po::value<int>(&opt)->default_value(10), "optimization level") ("include-path,I", po::value< vector<string> >(), "include path") ("input-file", po::value< vector<string> >(), "input file") ;
- help:就像上個範例一樣。最好讓每個 case 都有自己的 help option。
- optimization:這裡的 optimization 有兩個特點:
- 我們在程式裡頭指定了變數的位址(&opt)。如此一來,當 values 被分析儲存後,我們就可以透過該變數取得 optimization 的 value。
- 我們指定了變數的 default value 為 10 。因此,當使用者沒有給 optimization 指定一個 value 時, value 會是 10。
- include-path:include-path 在這裡是一個特殊的例子,它只從 command line 讀入資料來使用 options_description。(原文是:The "include-path" option is an example of the only case where the interface of the options_description class serves only one source -- the command line. 看了上下文,總覺得這句有點詭異、跳 tone)。對於常用到的 options,使用者往往喜歡使用它們的 short option name 形式,而這也就是程式碼裡頭 "include-path,I" option name 想表達的意思:"I" 是 include-path 的 short name。
另外,值得注意的是"include-path"
的型別是 std::vector。Boost.Program_options 對於 vectors 有特別的支援,它可以多次出現在 option 輸入中,而所有的 values 都會被存放在一個 vector 裡頭。 - input-file:描述了一連串、將被處理的檔案。當然,一開始的時候像下面的方式指定 input file 是可以的:
compiler --input-file=a.cpp
但,如果和下面的方法相比,上面的方式似乎就有點不標準囉!
compiler a.cpp
我們將針對這問題來探討一下:
像 a.cpp 這種在 parsing 的時候找不到 option name 的 token 的,在 Boost.Program_options 裡頭稱為 positional options。透過一些使用者的幫助,Boost.Program_options 有辦法可以知道 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);
開始兩行程式碼的意思是:所有的 positional options 需要被轉會成 "input-file" options。此外,這邊我們使用 command_line_parser 類別,而不是 parse_command_line function。parse_command_line 是一個只在簡單情況下會用到的 wrapper function 而已,但我們現在需要更多的資訊來做正確分析。
現在,所有的 options 都已經被描述和分析了,我們就饒了自己吧,不要去管怎麼實作 compiler 的邏輯、細節,先用 print 的方式來表示各個 options。
if (vm.count("include-path")) { cout << "Include paths are: " << vm["include-path"].as< vector<string> >() << "\n"; } if (vm.count("input-file")) { cout << "Input files are: " << vm["input-file"].as< vector<string> >() << "\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"來指定,而 Usage 也是這樣寫的,這可能會混淆了我們的使用者,如果能把這資訊隱藏應該會不錯,不過就留到下一個例子吧。
Multiptles Sources
很快地,妳/你就會發現都得把所有的 compiler options 填好是一件令人抓狂的事。想想看,難道妳/你希望使用者裝了一個新的 library 後每次使用妳/你的 compiler 都得一個一個把 options 填好嗎?如果能她/他決定了一些選項是每次都要使用到的呢?最好的情況是產生一個 common settings 然後搭配 command line 來使用。
當然,把 config file 跟 command line 搭配使用是有必要的。舉例來說:config file 的 optimization level 會被 command line 的輸入蓋過;然而 include path 卻是需要有累加效果的。
來看個 code 先,完整的程式可以在
example/multiple_sources.cpp
找到。option 定義有兩個有趣的點:
- 我們宣告了多個 options_descriptions 實體(instances)。為什麼呢?因為不是所有的 options 都是相近的,有些 options,像是 input-file 不應該出現在自動化 help message 裡頭;有些 options 只在 config files 裡頭才有意義。
- 最後,讓 help message 有點結構化、而不只是串列表是不錯的主意。
下面讓我們宣告幾個 option groups 吧:
// 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<int>(&opt)->default_value(10), "optimization level") ("include-path,I", po::value< vector<string> >()->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<string> >(), "input file") ;
請注意到宣告 include-path option 時呼叫的 composing method,這告訴了 library 不同來源的 values 應該被組合在一起(composed together)。
我們可以用 options_description 的 add method 來進一步的組織 option groups 中:
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);
分析和儲存 values 跟以往的作法相同,除了我們得額外呼叫了 parse_config_file 和呼叫 store 兩次之外。如果有些 value 在 command line 和 config file 中都出現了,那會發生什麼事?通常來說,先儲存下來的 value 會被視為優先,這也是 -optimization option 的設計,而對於 composing options 來說(像是 include-file ),這些 values 則是會被合併。
下面是個編譯、執行這個範例的方法:
$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
第一個呼叫是使用 config file。第二個則多使用了 command line 的輸入。正如我們所看到的,include path 的 value 是從 command line 和 config file 合併來的,而 optimization 則是從 command line 取得。
Reference
- Boost 1.39.0 Doc: Chapter 13. Boost.Program_options
1 則留言:
搶頭香!Keiko又有翻譯大作了~
張貼留言