次に-O,-Iオプション。これらはそれぞれint,string型のオプション値が後続します。さらに-Oには指定しない場合のデフォルト値がありますし、-Iは複数指定できます。コードはこんなカンジ:
#include <iostream>
#include <vector>
#include <string>
#include <boost/program_options.hpp>
namespace po = boost::program_options;
using namespace std;
int main(int argc, char* argv[]) {
try {
po::options_description desc("オプションの説明");
desc.add_options()
("help,h" , "ヘルプの出力")
("optimize,O" , po::value<int>()->value_name("<N>")->default_value(0), "最適化レベル")
("include,I" , po::value<vector<string>>()->value_name("<path>"), "インクルード・パス")
;
po::command_line_parser parser(argc,argv);
parser.options(desc);
po::variables_map vm;
po::store(parser.run(), vm);
po::notify(vm);
if ( vm.count("help") ) {
cout << "mycc [option]... <input_file>...\n"
<< desc << endl;
return 0;
}
cout << "optimize level=" << vm["optimize"].as<int>() << endl;
if ( vm.count("include") ) {
vector<string> includes = vm["include"].as<vector<string>>();
cout << "include = ";
for ( string path : includes ) cout << path << ' ';
cout << endl;
}
return 0;
} catch(exception& e) {
cerr << "error: " << e.what() << "\n";
} catch(...) {
cerr << "Exception of unknown type!\n";
}
return 1;
}
options_descriptionのadd_options()に続くオプション設定の第2引数にvalue<型>()を置くことでオプション値が後続することを教えてあげます。--include/-Iに対しては、オプション値の型をvector<string>とすることで複数指定可です。
オプション値はvariables_map vmに対してvm["オプション名"].as<型>()で取得できます。
myccがコンパイル対象とするfoo.cのような入力ファイルには-,--で始まるオプションスイッチがありません。Boost.program_optionsでは、オプションスイッチを持たないオプションをpositional optionと称しています。positional optionの解析はこんなカンジ:
...
po::options_description visible("オプションの説明");
visible.add_options()
("help,h" , "ヘルプの出力")
("compile,c" , "コンパイルのみ")
("optimize,O" , po::value<int>()->value_name("<N>")->default_value(0), "最適化レベル")
("include,I" , po::value<vector<string>>()->value_name("<path>"), "インクルード・パス")
;
po::options_description hidden("Hidden options");
hidden.add_options()
("input_file", po::value<vector<string>>(), "入力ソースコード")
;
// visible と hidden を足し合わせて desc を作る
po::options_description desc;
desc.add(visible).add(hidden);
// オプションスイッチのないオプション pos
po::positional_options_description pos;
pos.add("input_file",-1);
// desc と pos をパーサーに食わす
po::command_line_parser parser(argc,argv);
parser.options(desc).positional(pos);
po::variables_map vm;
po::store(parser.run(), vm);
po::notify(vm);
typedef boost::optional<po::variable_value> option_t;
auto get = [&vm](const string& name) {
return vm.count(name) ? option_t(vm[name]) : option_t();
};
option_t compile_opt = get("compile");
option_t optimize_opt = get("optimize");
option_t include_opt = get("include");
option_t input_opt = get("input_file");
if ( get("help") ) {
cout << "mycc [option]... <input_file>...\n"
<< visible << endl;
return 0;
}
if ( compile_opt ) { cout << "compile only" << endl; }
if ( optimize_opt ) { cout << "optimize = " << optimize_opt->as<int>() << endl; }
if ( include_opt ) {
vector<string> includes = include_opt->as<vector<string>>();
cout << "include = ";
for ( string path : includes ) cout << path << ' ';
cout << endl;
}
if ( input_opt ) {
vector<string> inputs = input_opt->as<vector<string>>();
cout << "input files = ";
for ( string file : inputs ) cout << file << ' ';
cout << endl;
}
...
options_description visibleとhiddenを足し合わせてdescを作っています。こうすることで--help時にvisibleのみをヘルプ出力の対象にして、ヘルプに現れない隠しオプションが実現できます。
オプション値の取得は、Boost.optionalと小さなラムダ式で少しばかりコードを涼しくしました。


