使いやすく、強力で表現力豊かな C++11/14/17 のコマンド ライン引数処理が1 つのヘッダー ファイルに含まれています。
オプション、オプション + 値、位置値、位置コマンド、ネストされた代替案、デシジョン ツリー、結合可能なフラグ、カスタム値フィルターなど
ドキュメントの生成 (使用行、マニュアルページ)。エラー処理
たくさんの例。大規模なテストセット
次のコマンド ライン インターフェイスを考えてみましょう。
概要 変換 <入力ファイル> [-r] [-o <出力形式>] [-utf16] オプション -r, --recursive ファイルを再帰的に変換します -utf16 は UTF-16 エンコーディングを使用します
以下は、位置値input file
と 3 つのオプション-r
、 -o
、 -utf16
を定義するコードです。解析が失敗した場合、上記のデフォルトの man ページのようなスニペットが stdout に出力されます。
#include <iostream>#include "clipp.h" 名前空間 Clipp を使用; std::cout を使用します。 std::string;int main(int argc, char* argv[]) { bool rec = false, utf16 = false; を使用します。 string infile = "", fmt = "csv";auto cli = (value("入力ファイル", infile),option("-r", "--recursive").set(rec).doc("ファイルの変換再帰的に"),option("-o") & value("出力形式", fmt),option("-utf16").set(utf16).doc("use UTF-16エンコーディング」) );if(!parse(argc, argv, cli)) cout << make_man_page(cli, argv[0]);// ...}
概要 finder make <単語ファイル> -dict <辞書> [--progress] [-v] finder find <infile>... -dict <dictionary> [-o <outfile>] [-split|-nosplit] [-v] ファインダーヘルプ [-v] オプション --progress、-p 進行状況を表示 -o, --output <outfile> stdout ではなくファイルに書き込みます -split、-nosplit (分割しない) 出力 -v、--version バージョンを表示
この CLI には、3 つの代替コマンド ( make
、 find
、 help
)、いくつかの位置値引数 ( <wordfile>
、 <infile>
) (そのうちの 1 つは繰り返し可能)、値引数を持つ必須のフラグ ( -dict <dictionary>
)、値と引数を指定したオプション ( -o <outfile>
)、2 つの代替オプションを備えた 1 つのオプション ( -split
、 -nosplit
)、および 2 つの従来のオプション ( -v
、 --progress
)。
以下は、インターフェースを定義し、上記の man ページのスニペットを生成し、解析結果を処理するコードです。
名前空間クリップを使用します。 std::cout を使用します。 using std::string;//解析結果を格納する変数;デフォルト値senum クラスモード {make, find, help} で初期化されます。 選択されたモード = モード::ヘルプ; std::vector<string> 入力; string dict, out;bool Split = false, progr = false;autodictionary = required("-dict") & value("dictionary", dict);auto makeMode = (command("make").set(selected,mode) ::作る)、 値("ワードファイル", 入力), 辞書、 option("--progress", "-p").set(progr) % "進行状況を表示" );auto findMode = (command("find").set(selected,mode::find), 値("infile", 入力), 辞書、 (option("-o", "--output") & value("outfile", out)) % "標準出力の代わりにファイルに書き込みます", (option("-split" ).set(split,true) | option("-nosplit").set(split,false) ) % "出力を分割しません" );auto cli = ( (makeMode | findMode | command("help").set(selected,mode::help) ),option("-v", "--version").call([]{cout << "バージョン 1.0nn" ;}).doc("バージョンの表示") );if(parse(argc, argv, cli)) {switch(selected) {case mode::make: /* ... */ Break;case mode::find: /* ... */ Break;case mode::help: cout << make_man_page(cli, "finder");壊す; } } それ以外 { cout << use_lines(cli, "finder") << 'n'; }
以下に、clipp がどのように機能するかを理解するためのいくつかの例を示します。コマンド ライン引数を使用して設定するいくつかの変数を含むこの基本的な設定を考えてみましょう。
int main(int argc, char* argv[]) { using namespace Clipp;// いくつかの変数を定義する bool a = false, b = false;int n = 0, k = 0;double x = 0.0, y = 0.0; std::vector<int> ids;auto cli = ( /* コマンド ライン インターフェイスを定義するコードがここにあります */ );parse(argc, argv, cli); // argv[0]std::cout << use_lines(cli, "exe") << 'n'; を除外します。 }
インターフェース ( usage_lines ) | コード ( cli 括弧の内容) |
---|---|
exe [-a] | option("-a", "--all").set(a) |
exe [--all] | option("--all", "-a", "--ALL").set(a) |
exe [-a] [-b] | option("-a").set(a), option("-b").set(b) |
exe -a | required("-a").set(a) |
exe [-a] -b | option("-a").set(a), required("-b").set(b) |
exe [-n <times>] | option("-n", "--iter") & value("times", n) |
exe [-n [<times>]] | option("-n", "--iter") & opt_value("times", n) |
exe -n <times> | required("-n", "--iter") & value("times", n) |
exe -n [<times>] | required("-n", "--iter") & opt_value("times", n) |
exe [-c <x> <y>] | option("-c") & value("x", x) & value("y", y) |
exe -c <x> <y> | required("-c") & value("x", x) & value("y", y) |
exe -c <x> [<y>] | required("-c") & value("x", x) & opt_value("y", y) |
exe [-l <lines>...] | option("-l") & values("lines", ids) |
exe [-l [<lines>...]] | option("-l") & opt_values("lines", ids) |
exe [-l <lines>]... | repeatable( option("-l") & value("lines", ids) ) |
exe -l <lines>... | required("-l") & values("lines", ids) |
exe -l [<lines>...] | required("-l") & opt_values("lines", ids) |
exe (-l <lines>)... | repeatable( required("-l") & value("lines", ids) ) |
exe fetch [-a] | command("fetch").set(k,1), option("-a").set(a) |
exe init | fetch [-a] | command("init").set(k,0) | (command("fetch").set(k,1), option("-a").set(a)) |
exe [-a|-b] | option("-a").set(a) | option("-b").set(b) |
exe [-ma|b] | option("-m") & (required("a").set(a) | required("b").set(b)) |
各トピックの詳細な説明については、例のセクションを参照してください。
読みやすくするために、すべての例では名前空間修飾子が省略されています。すべてのエンティティはnamespace clipp
で定義されます。
int main(int argc, char* argv[]) { using namespace Clipp;auto cli = ( /* コード定義のコマンド ライン インターフェイスがここにあります */ );parse(argc, argv, cli); //argv[0]を除外します//argv[0]を含めたい場合//parse(argv, argv+argc, cli);}
コマンド ライン インターフェイスの構成要素には、パラメーターとグループの 2 種類があります。便宜的な名前を付けたファクトリー関数により、必要な設定が適用されたパラメーターまたはグループが生成されます。
ブール a = false、f = false; 文字列 s; Vector<string> vs;auto cli = ( // 必須の位置に一致しますrepeatablecommand("push"), // 正確に Yes Yes norequired("-f", "--file").set(f), // 正確に Yes no norequired("-a", "--all", "-A").set(a), // まさに no no no value("file", s), // 任意の引数 Yes Yes novalues("file", vs), // 任意の引数 Yes Yes Yesopt_value("file", s), // 任意の引数 no Yes noopt_values("file" , vs), // 任意の引数 no yes yes//「catch all」パラメータ - エラー処理に便利any_other(vs), // 任意の引数 no no yes//catches述語を満たし、他のパラメータと一致しない引数any(predicate, vs) // predicate no no yes);
上記の関数は便利なファクトリーです。
ブール f = true; string s;auto v1 = value("file", s);// は以下と同等です:auto v2 =parameter{match::nonempty}.label("file").blocking(true).repeatable(true).set (s);auto r1 = required("-f", "--file").set(f);// と同等です:auto r2 =parameter{"-f", "--file"}.required(true).set(f);
必須パラメータは少なくとも 1 つのコマンドライン引数と一致する必要があります
反復可能なパラメータは任意の数の引数に一致します
非位置 (= 非ブロッキング) パラメーターは、任意の順序で引数と一致できます
位置 (ブロック) パラメータは「停止点」を定義します。つまり、それが一致するまで、それに続くすべてのパラメータの一致は許可されません。一致すると、その前にある (現在のグループ内で) すべてのパラメータに到達できなくなります。
パラメーターを順番に一致させたい場合は、 operator &
またはグループ化関数in_sequence
使用してパラメーターを結び付けることができます。
int n = 1;文字列 s; Vector<int> ls;auto cli = (//必須の値を持つオプションoption("-n", "--repeat") & value("times", n),//オプションの値を持つ必須のフラグrequired("--file ") & opt_value("name", s), //ちょうど 2 つの値を持つオプションoption("-p", "--pos") & value("x") & value("y"),//前と同じ v vin_sequence( option("-p", "--pos") , value("x") , value("y") ), //少なくともオプション1 つの値 (およびオプションで複数)option("-l") & value("lines", ls) );
値パラメーターはフィルター関数を使用して、引数文字列との一致が許可されているかどうかをテストします。 value
、 values
、 opt_value
、およびopt_values
で使用されるデフォルトのフィルターmatch::nonempty
、空でない引数文字列と一致します。他のフィルター関数/関数オブジェクトをvalue
、 values
などの最初の引数として指定することも、最も一般的なケースをカバーするこれらの組み込みの短縮ファクトリ関数のいずれかを使用することもできます。
文字列名;二重 r = 0.0; int n = 0;auto cli = (value("user", name), // 空でない任意の stringword("user", name) に一致します, // 空でない任意の英数字 stringnumber("ratio", r) に一致します, // 数値の文字列表現と一致しますinteger("times", n) // 整数の文字列表現と一致します);
value
、 opt_value
などと同様に、 words
、 opt_word
などの関数もあります。
auto is_char = [](const string& arg) { return arg.size() == 1 && std::isalpha(arg[0]); };char c = ' '; // 必要な位置のrepeatablevalue(is_char, "c", c)に一致します。 // 1 文字はいはいいいえ
相互に互換性のあるパラメータを括弧とカンマでグループ化します。
auto cli = ( オプション("-a"), オプション("-b"), オプション("-c") );
operator |
使用して、相互に排他的なパラメータを代替としてグループ化します。またはone_of
:
auto cli1 = ( value("input_file") | command("list") | command("flush") );auto cli2 = one_of( value("input_file") , command("list") , command("flush" ) );
パラメータをグループ化して、 operator &
またはin_sequence
を使用して順番に一致させる必要があります。
double x = 0, y = 0, z = 0;auto cli1 = (option("-pos") & value("X",x) & value("Y",y) & value("Z",z) ) );auto cli2 = in_sequence( option("-pos") , value("X",x) , value("Y",y) , value("Z",z) );
周囲のグループはこれによる影響を受けないので、 -a
と-b
任意の順序で一致できますが、 -b
と値X
順番に一致する必要があることに注意してください。
ブール a = false、b = false; int x = 0;auto cli = (option("-a").set(a), option("-b").set(b) & value("X",x) );
グループをネストして組み合わせて、任意の複雑なインターフェイスを形成できます (こことここを参照)。
auto cli = ( command("push") | ( command("pull"), option("-f", "--force") ) );
グループも繰り返し可能です。
auto cli1 =repeatable( command("flip") | command("flop") );
フラグのグループに共通の接頭辞を強制します。
int x = 0;auto cli1 = with_prefix("-", option("a"), option("b") & value("x",x), ... ); // => -a -b ^影響を受けません^auto cli2 = with_prefix_short_long("-", "--", option("a", "all"), option("b"), ... ); // => -a --all -b
フラグのグループに共通の接尾辞を強制します。
int x = 0;auto cli1 = with_suffix("=, option("a") & value("x",x), ... ); // => a= ^影響を受けません^auto cli2 = with_suffix_short_long(":", ":=, option("a", "all"), option("b"), ... ); // => a: すべて:= b:
フラグのグループを結合可能にします。
auto cli1 = joinable( オプション("-a"), オプション("-b")); //「-a」、「-b」、「-ab」、「-ba」と一致します//任意の共通プレフィックスでも動作します:auto cli2 = joinable( option("--xA0"), option("- -xB1")); //「--xA0B1」または「--xB1A0」にも一致します
コマンド ライン インターフェイスをコードの残りの部分に接続する最も簡単な方法は、オブジェクトの値または関数 (オブジェクト) の呼び出しをパラメーターにバインドすることです (こちらも参照)。
ブール b = false; int i = 5; int m = 0;文字列x; ifstream fs;auto cli = ( option("-b").set(b), // "-b" が検出されました -> b を trueoption("-m").set(m,2), // " -m" が検出されました -> m を 2option("-x") & value("X", x) に設定します、 // 引数文字列から x の値を設定します option("-i") & opt_value("i", i) 、 // 引数文字列から i の値を設定します option("-v").call( []{ cout << "v"; } ), // 関数を呼び出します (オブジェクト) / lambdaoption("-v")( []{ cout << "v" } ), // 前と同じ lineoption("-f") & value("file").call([&](string f){ fs.open(f); }) );
実稼働コードでは、おそらく設定クラスを使用するでしょう。
構造体の設定 { bool x = false; /* ... */ }; 設定 cmdline_settings(int argc, char* argv[]) { 設定 s;auto cli = ( option("-x").set(sx), /* ... */ );parse(argc, argv, cli);return s; }
ターゲットは次のいずれかである必要があることに注意してください。
基本型 ( int, long int, float, double, ...
)
const char*
から変換可能な型
呼び出し可能なエンティティ: 空のパラメーター リスト、またはconst char*
から変換可能なパラメーターを 1 つだけ持つ関数、関数オブジェクト / ラムダ
グループおよびパラメータの docstring は、メンバー関数doc
またはoperator %
を使用して設定できます。
自動クリ = ( ( option("x").set(x).doc("sets X"),option("y").set(y) % "sets Y" )、 "文書化されたグループ 1:" % ( option("-g").set(g).doc("activate G"), option("-h").set(h) % "H を有効にする" )、 ( option("-i").set(i) % "I をアクティブにする", option("-j").set(j) % "J をアクティブ化" ).doc("文書化されたグループ 2:") );
使用ライン:
cout << use_lines(cli, "progname") << 'n';//フォーマットオプション付きauto fmt = doc_formatting{} .first_column(3) .last_column(79); cout << use_lines(cli, "progname", fmt) << 'n';
詳細なドキュメント:
cout << document(cli) << 'n';//フォーマットオプション付きauto fmt = doc_formatting{} .first_column(7) .doc_column(15) .last_column(99); cout << ドキュメント(cli, fmt) << 'n';
マニュアルページ:
auto cli = ( /*コマンドラインインターフェイスを定義するコードがここにあります*/ ); cout << make_man_page(cli, "progname") << 'n';//フォーマットオプション付きauto fmt = doc_formatting{} .first_column(7) .doc_column(15) .last_column(99); cout << make_man_page(cli, "progname", fmt) << 'n';
各パラメーターにはイベント ハンドラー関数を関連付けることができます。これらは、パラメータにマップされている引数ごとに 1 回 (または欠落しているイベントごとに 1 回) 呼び出されます。
string file = "default.txt";auto param = required("-nof").set(file,"") | required("-f") & value("file", file) // 2 番目、3 番目、4 番目、... で一致 (この場合はエラーになります) .if_repeat( [] { /* ... */ } ) // 必要な値パラメータが欠落している場合 .if_missing( [] { /* ... */ } ) // 到達できない場合、たとえばファイル名の前にフラグ「-f」がない場合 .if_blocked( [] { /* ... */ } ) // 一致が他の代替 "-nof" と競合する場合 .if_conflicted( [] { /* ... */ } );
ハンドラー関数は、イベントが最初に発生した引数のインデックスに設定される int を取ることもできます。
string file = "default.txt";auto param = required("-nof").set(file,"") | required("-f") & value("file", file) .if_repeat ( [] (int argIdx) { /* ... */ } ) .if_missing ( [] (int argIdx) { /* ... */ } ) .if_blocked ( [] (int argIdx) { /* ... */ } ) .if_conflicted( [] (int argIdx) { /* ... */ } );
次の CLI のコマンド ライン引数として-f -b
または-b -f -a
を指定すると、 -f
の後の値はオプションではないため、エラーが報告されます。
auto cli = ( オプション("-a"), オプション("-f") & 値("ファイル名"), オプション("-b") );
この動作は、ほとんどのユースケースでは問題ありません。しかし、ファイル名がフラグ名と衝突する可能性があるため、プログラムで任意の文字列をファイル名として取りたい場合はどうすればよいでしょうか? operator !
を使用して value パラメータを貪欲にすることができます。 。このようにすると、 -f
指定されるとすぐに、 -f
の後の次の文字列が常に最高の優先順位で照合されます。
auto cli = ( オプション("-a"), オプション("-f") & !value("ファイル名"), オプション("-b") );// ^~~~~~
貪欲なパラメータには十分注意してください。
auto cli = ( /* ここにインターフェース */ );auto res = parse(argc, argv, cli);if(res.any_error()) { /* ... */ }//集約エラーif(res.unmapped_args_count ()) { /* ... */ }if(res.any_bad_repeat()) { /* ... */ }if(res.any_blocked()) { /* ... */ }if(res.any_conflict()) { /* ... */ }