CommandLine 2.0 ライブラリ マニュアル

はじめに

このドキュメントでは、CommandLine 引数処理ライブラリについて説明します。使用方法と機能を紹介します。CommandLine ライブラリは、プログラムが受け取るコマンドラインオプションを指定するための宣言的なアプローチを使用しています。デフォルトでは、これらのオプション宣言は、宣言されたオプションに対して解析された値を暗黙的に保持します(もちろん、これは変更できます)。

多くの異なる言語で非常に多くのコマンドライン引数解析ライブラリがありますが、私のニーズに合致するものは1つもありませんでした。他のライブラリの機能と問題を検討することで、CommandLine ライブラリを次の機能を持つように設計しました。

  1. 速度:CommandLine ライブラリは非常に高速で、リソースをほとんど消費しません。ライブラリの解析時間は、認識されるオプションの数ではなく、解析される引数の数に正比例します。さらに、コマンドライン引数の値は、ユーザー定義のグローバル変数に透過的に取得され、他の変数と同じように(そして同じパフォーマンスで)アクセスできます。

  2. 型安全:CommandLine のユーザーは、必要な引数の型(int?string?bool?enum?)、そしてその型変換を覚えておく必要はありません。これにより、エラーが発生しやすい構成を防ぐだけでなく、ソースコードが大幅にクリーンになります。

  3. サブクラスは不要:CommandLine を使用するには、取得したい引数に対応する変数をインスタンス化します。パーサーのサブクラスを作成する必要はありません。つまり、一切のボイラープレートコードを記述する必要がありません。

  4. グローバルアクセス可能:ライブラリは、ライブラリにリンクするすべてのツールで自動的に有効になるコマンドライン引数を指定できます。これは、アプリケーションがパーサーに渡す引数のリストを保持する必要がないためです。これにより、動的にロードされたオプションのサポートも容易になります。

  5. クリーン:CommandLine は enum やその他の型を直接サポートしているため、ライブラリにエラーが少なく、セキュリティが向上します。整数型のコマンドライン引数に、enum 型で無効な値が誤って割り当てられることを心配する必要はありません。

  6. 強力:CommandLine ライブラリは、単純なブールフラグからスカラー引数文字列整数列挙型倍精度浮動小数点数)、引数のリストまで、さまざまな種類の引数をサポートしています。これは、CommandLine が…であるためです。

  7. 拡張可能:CommandLine に新しい引数型を追加することは非常に簡単です。宣言時にコマンドラインオプションで使用したいパーサーを指定するだけです。カスタムパーサーも問題ありません。

  8. 省力化:CommandLine ライブラリは、ユーザーである皆さんの負担となる雑務を削減します。たとえば、ツールで使用可能なコマンドラインオプションを表示する-helpオプションを自動的に提供します。さらに、基本的な正確性のチェックの大部分を自動で行います。

  9. 高度な機能:CommandLine ライブラリは、実際のプログラムでよく見られるさまざまな形式のオプションを処理できます。たとえば、位置引数、ls スタイルのグループ化オプション( ' ls -lad 'を自然に処理するため)、ld スタイルのプレフィックスオプション( ' -lmalloc -L/usr/lib 'を解析するため)、およびインタープリタースタイルのオプションです。

このドキュメントは、皆さんがすぐに、そして簡単にユーティリティで CommandLine を使い始めるのに役立つでしょう。また、機能の仕組みを理解するための簡単なリファレンスマニュアルにもなります。

クイックスタートガイド

このマニュアルのセクションでは、基本的なコンパイラツールの簡単な CommandLine 化について説明します。これは、皆さん自身のプログラムで CommandLine ライブラリの使い方を理解し、その優れた機能の一部を紹介することを目的としています。

まず、プログラムに CommandLine ヘッダーファイルを含める必要があります。

#include "llvm/Support/CommandLine.h"

さらに、メインプログラムの最初の行としてこれを追加する必要があります。

int main(int argc, char **argv) {
  cl::ParseCommandLineOptions(argc, argv);
  ...
}

…これは実際には引数を解析し、変数宣言に値を入力します。

コマンドライン引数をサポートする準備が整ったので、システムにどの引数を、どのような種類の引数として扱うかを伝える必要があります。CommandLine ライブラリは、解析された値を取得するグローバル変数宣言を使用して、コマンドライン引数をモデル化する宣言的な構文を使用します。つまり、サポートしたいコマンドラインオプションごとに、結果を取得するためのグローバル変数宣言が必要です。たとえば、コンパイラでは、Unix 標準の ' -o <filename> 'オプションを使用して、出力ファイルの場所を指定したいと考えています。CommandLine ライブラリでは、これは次のように表されます。

cl::opt<string> OutputFilename("o", cl::desc("Specify output filename"), cl::value_desc("filename"));

これにより、 " o " 引数(最初の引数)の結果を取得するために使用されるグローバル変数 " OutputFilename " が宣言されます。" cl::opt " テンプレート( " cl::list " テンプレートとは対照的に)を使用して、これが単純なスカラーオプションであることを指定し、CommandLine ライブラリに解析するデータ型が文字列であることを伝えます。

2 番目と 3 番目の引数(オプション)は、 " -help " オプションに出力する内容を指定するために使用されます。この場合、次のような行が表示されます。

USAGE: compiler [options]

OPTIONS:
  -h                - Alias for -help
  -help             - display available options (-help-hidden for more)
  -o <filename>     - Specify output filename

string データ型を使用してコマンドラインオプションを解析するように指定したので、宣言された変数は、通常の C++ 文字列オブジェクトが使用できるすべてのコンテキストで、実際の文字列として使用できます。たとえば

...
std::ofstream Output(OutputFilename.c_str());
if (Output.good()) ...
...

コマンドラインオプション処理ライブラリをカスタマイズするために使用できる多くの異なるオプションがありますが、上記の例はこれらのオプションの一般的なインターフェースを示しています。オプションは任意の順序で指定でき、cl::desc(…) などのヘルパー関数を使用して指定されるため、覚えておく必要がある位置の依存関係はありません。使用可能なオプションについては、リファレンスガイドで詳しく説明します。

例を続けると、コンパイラは入力ファイル名と出力ファイル名を受け取るようにしたいのですが、入力ファイル名をハイフンで指定したくありません(つまり、 -filename.c ではありません)。このスタイルの引数をサポートするために、CommandLine ライブラリでは、プログラムに対して位置引数を指定できます。これらの位置引数は、オプション形式ではないコマンドラインパラメータで満たされます。この機能は次のように使用します。

cl::opt<string> InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"));

この宣言は、最初的位置引数を入力ファイル名として扱うことを示しています。ここでは、cl::init オプションを使用して、コマンドラインオプションの初期値を指定しています。これは、オプションが指定されていない場合に使用されます(オプションに対して cl::init 修飾子を指定しない場合、データ型のデフォルトコンストラクターを使用して値が初期化されます)。コマンドラインオプションはデフォルトでオプションになっているため、ユーザーが入力ファイル名を常に指定するようにしたい場合は、cl::Required フラグを追加し、cl::init 修飾子を削除できます。次のように。

cl::opt<string> InputFilename(cl::Positional, cl::desc("<input file>"), cl::Required);

繰り返しになりますが、CommandLine ライブラリはオプションを特定の順序で指定する必要がないため、上記の宣言は次の宣言と同じです。

cl::opt<string> InputFilename(cl::Positional, cl::Required, cl::desc("<input file>"));

cl::Required フラグを追加するだけで、引数が指定されていない場合に CommandLine ライブラリが自動的にエラーを発行するため、コマンドラインオプションの検証コード全体がアプリケーションからライブラリにシフトされます。これは、フラグを使用してライブラリのデフォルトの動作をオプションごとに変更する方法のほんの一例です。上記の宣言のいずれかを追加することにより、 -help オプションの要約は次のように拡張されます。

USAGE: compiler [options] <input file>

OPTIONS:
  -h                - Alias for -help
  -help             - display available options (-help-hidden for more)
  -o <filename>     - Specify output filename

…入力ファイル名が期待されていることを示しています。

ブール引数

入力ファイル名と出力ファイル名に加えて、コンパイラの例では、バイナリ出力をターミナルに強制的に書き込む " -f "、静止モードを有効にする " --quiet "、および一部のユーザーとの下位互換性のための " -q " の 3 つのブールフラグをサポートしたいと思います。これらは、次のようにブール型のオプションを宣言することでサポートできます。

cl::opt<bool> Force ("f", cl::desc("Enable binary output on terminals"));
cl::opt<bool> Quiet ("quiet", cl::desc("Don't print informational messages"));
cl::opt<bool> Quiet2("q", cl::desc("Don't print informational messages"), cl::Hidden);

これは期待通りに動作します。3つのブール変数("Force"、"Quiet"、"Quiet2")を宣言し、これらのオプションを認識します。"-q"オプションは" cl::Hidden"フラグを使用して指定されています。この修飾子は、標準の"-help"出力に表示されないようにします("-help-hidden"出力には表示されます)。

CommandLineライブラリは、異なるデータ型に対して異なるパーサーを使用します。例えば、文字列の場合、オプションに渡された引数は文字列変数の内容にそのままコピーされます…ブール値の場合は明らかにそうすることはできませんので、よりスマートなパーサーを使用する必要があります。ブールパーサーの場合、オプションを許容しません(その場合、変数にtrueの値を代入します)、または値"true"または"false"を指定できます。以下のいずれかの入力を許可します。

compiler -f          # No value, 'Force' == true
compiler -f=true     # Value specified, 'Force' == true
compiler -f=TRUE     # Value specified, 'Force' == true
compiler -f=FALSE    # Value specified, 'Force' == false

…以降同様です。boolパーサーは文字列値をブール値に変換し、"compiler -f=foo"のようなものを拒否します。同様に、floatdoubleintパーサーは、文字列値を指定されたデータ型に解析するために'strtol'と'strtod'Cライブラリ呼び出しを使用して、期待通りに動作します。

上記の宣言により、"compiler -help"は以下を出力します。

USAGE: compiler [options] <input file>

OPTIONS:
  -f     - Enable binary output on terminals
  -o     - Override output filename
  -quiet - Don't print informational messages
  -help  - display available options (-help-hidden for more)

そして"compiler -help-hidden"は以下を出力します。

USAGE: compiler [options] <input file>

OPTIONS:
  -f     - Enable binary output on terminals
  -o     - Override output filename
  -q     - Don't print informational messages
  -quiet - Don't print informational messages
  -help  - display available options (-help-hidden for more)

この簡単な例では、' cl::opt'クラスを使用して単純なスカラーコマンドライン引数を解析する方法を示しました。単純なスカラー引数に加えて、CommandLineライブラリは、CommandLineオプションエイリアスとオプションのリストをサポートするためのプリミティブも提供します。

引数エイリアス

これまでの例は、静かな状態をこのように確認する必要がある点を除けば、うまく動作します。

...
  if (!Quiet && !Quiet2) printInformationalMessage(...);
...

…これは非常に面倒です!同じ条件に対して2つの値を定義する代わりに、" cl::alias"クラスを使用して、"-q"オプションを"-quiet"オプションの**エイリアス**にすることができます。

cl::opt<bool> Force ("f", cl::desc("Overwrite output files"));
cl::opt<bool> Quiet ("quiet", cl::desc("Don't print informational messages"));
cl::alias     QuietA("q", cl::desc("Alias for -quiet"), cl::aliasopt(Quiet));

3行目(上記から変更した唯一の行)は、指定された場合に"Quiet"変数(cl::aliasopt修飾子で指定)を更新する"-q"エイリアスを定義します。エイリアスは状態を保持しないため、プログラムが問い合わせる必要があるのはQuiet変数だけです。エイリアスのもう1つの優れた機能は、"-help"出力から自動的に非表示になることです(ただし、"-help-hidden 出力"には引き続き表示されます)。

これで、アプリケーションコードは単純に以下を使用できます。

...
  if (!Quiet) printInformationalMessage(...);
...

…これははるかに優れています!" cl::alias"は、任意の変数型に代替名を与えるために使用でき、多くの用途があります。

可能性のある選択肢から代替案を選択する

これまで、CommandLineライブラリがstd::stringboolintのような組み込み型をどのように処理するかを見てきましたが、列挙型や'int*'など、ライブラリが認識していない型をどのように処理するのでしょうか?

答えは、(拡張ガイドで説明されているように、独自のパーサーを指定しない限り)、テーブル駆動型の汎用パーサーを使用することです。このパーサーは文字列を必要な型にマップし、このマッピングを指定する必要があります。

標準フラグ"-g"、"-O0"、"-O1"、"-O2"を使用して、最適化レベルを4つ追加したいとしましょう。上記のようにブールオプションで簡単に実装できますが、この戦略にはいくつかの問題があります。

  1. ユーザーは一度に複数のオプションを指定できます(例:"compiler -O3 -O2")。CommandLineライブラリは、この誤った入力を検出できません。

  2. 設定されている変数を4つテストする必要があります。

  3. これは、私たちが望む数値レベルにマップされません…そのため、あるレベルが"-O1"以上であるかどうかを簡単に確認できません。

これらの問題に対処するために、列挙値を使用し、CommandLineライブラリが適切なレベルを直接入力するようにすることができます。

enum OptLevel {
  g, O1, O2, O3
};

cl::opt<OptLevel> OptimizationLevel(cl::desc("Choose optimization level:"),
  cl::values(
    clEnumVal(g , "No optimizations, enable debugging"),
    clEnumVal(O1, "Enable trivial optimizations"),
    clEnumVal(O2, "Enable default optimizations"),
    clEnumVal(O3, "Enable expensive optimizations")));

...
  if (OptimizationLevel >= O2) doPartialRedundancyElimination(...);
...

この宣言は"OptLevel"列挙型の"OptimizationLevel"変数を定義します。この変数には、宣言にリストされている値のいずれかを代入できます。CommandLineライブラリは、ユーザーがオプションの1つだけを指定できることを強制し、有効な列挙値のみが指定されるようにします。"clEnumVal"マクロは、コマンドライン引数が列挙値と一致することを保証します。このオプションを追加すると、ヘルプ出力は次のようになります。

USAGE: compiler [options] <input file>

OPTIONS:
  Choose optimization level:
    -g          - No optimizations, enable debugging
    -O1         - Enable trivial optimizations
    -O2         - Enable default optimizations
    -O3         - Enable expensive optimizations
  -f            - Enable binary output on terminals
  -help         - display available options (-help-hidden for more)
  -o <filename> - Specify output filename
  -quiet        - Don't print informational messages

この場合、フラグ名が列挙名に直接対応しているため、プログラム内に"g"という名前の列挙型定義を望まない可能性があるため、少しぎこちなくなっています。そのため、この例を次のように記述することもできます。

enum OptLevel {
  Debug, O1, O2, O3
};

cl::opt<OptLevel> OptimizationLevel(cl::desc("Choose optimization level:"),
  cl::values(
   clEnumValN(Debug, "g", "No optimizations, enable debugging"),
    clEnumVal(O1        , "Enable trivial optimizations"),
    clEnumVal(O2        , "Enable default optimizations"),
    clEnumVal(O3        , "Enable expensive optimizations")));

...
  if (OptimizationLevel == Debug) outputDebugInfo(...);
...

"clEnumVal"の代わりに"clEnumValN"マクロを使用することで、フラグが取得する名前を直接指定できます。一般的に直接マッピングは良いのですが、マッピングを維持できない場合や維持したくない場合があり、そのような場合に使用します。

名前付きの代替案

もう1つの便利な引数の形式は、名前付きの代替スタイルです。コンパイラで、使用できるさまざまなデバッグレベルを指定するために、このスタイルを使用します。各デバッグレベルが独自のスイッチになるのではなく、一度に1つだけ指定できる次のオプションをサポートします。"--debug-level=none"、"--debug-level=quick"、"--debug-level=detailed"。これを行うには、最適化レベルフラグとまったく同じ形式を使用しますが、オプション名も指定します。この場合、コードは次のようになります。

enum DebugLev {
  nodebuginfo, quick, detailed
};

// Enable Debug Options to be specified on the command line
cl::opt<DebugLev> DebugLevel("debug_level", cl::desc("Set the debugging level:"),
  cl::values(
    clEnumValN(nodebuginfo, "none", "disable debug information"),
     clEnumVal(quick,               "enable quick debug information"),
     clEnumVal(detailed,            "enable detailed debug information")));

この定義は、"enum DebugLev"型の列挙されたコマンドライン変数を定義します。これは以前とまったく同じように機能します。ここでの違いは、プログラムのユーザーに公開されるインターフェースと"-help"オプションによるヘルプ出力だけです。

USAGE: compiler [options] <input file>

OPTIONS:
  Choose optimization level:
    -g          - No optimizations, enable debugging
    -O1         - Enable trivial optimizations
    -O2         - Enable default optimizations
    -O3         - Enable expensive optimizations
  -debug_level  - Set the debugging level:
    =none       - disable debug information
    =quick      - enable quick debug information
    =detailed   - enable detailed debug information
  -f            - Enable binary output on terminals
  -help         - display available options (-help-hidden for more)
  -o <filename> - Specify output filename
  -quiet        - Don't print informational messages

繰り返しますが、デバッグレベル宣言と最適化レベル宣言の唯一の構造上の違いは、デバッグレベル宣言にオプション名(""debug_level"")が含まれていることです。これは、ライブラリが引数を処理する方法を自動的に変更します。CommandLineライブラリは両方の形式をサポートしているので、アプリケーションに最適な形式を選択できます。

オプションのリストを解析する

標準的な引数型を片付けたので、少し大胆にいきましょう。最適化プログラムが実行する最適化の**リスト**を受け入れ、重複を許可したいとしましょう。たとえば、"compiler -dce -instsimplify -inline -dce -strip"を実行したい場合があります。この場合、引数の順序と出現回数は非常に重要です。これは"cl::list"テンプレートの目的です。まず、実行したい最適化の列挙型を定義します。

enum Opts {
  // 'inline' is a C++ keyword, so name it 'inlining'
  dce, instsimplify, inlining, strip
};

次に"cl::list"変数を定義します。

cl::list<Opts> OptimizationList(cl::desc("Available Optimizations:"),
  cl::values(
    clEnumVal(dce               , "Dead Code Elimination"),
    clEnumVal(instsimplify      , "Instruction Simplification"),
   clEnumValN(inlining, "inline", "Procedure Integration"),
    clEnumVal(strip             , "Strip Symbols")));

これは、概念的には"std::vector"型の変数を定義します。したがって、標準的なベクターメソッドでアクセスできます。

for (unsigned i = 0; i != OptimizationList.size(); ++i)
  switch (OptimizationList[i])
     ...

…を使用して、指定されたオプションのリストを反復処理します。

"cl::list"テンプレートは完全に汎用的であり、"cl::opt"テンプレートで使用できるデータ型やその他の引数で使用できます。リストを使用する特に便利な方法は、複数の位置指定引数が指定される可能性がある場合に、すべて的位置指定引数をまとめて取得することです。たとえば、リンカーの場合、リンカーはいくつかの'.o'ファイルを取り、それらをリストに取得する必要があります。これは自然に次のように指定されます。

...
cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<Input files>"), cl::OneOrMore);
...

この変数は"vector"オブジェクトのように機能します。そのため、上記の例のように、リストへのアクセスは簡単です。この例では、cl::OneOrMore修飾子を使用して、ユーザーがコマンドラインに'.o'ファイルを指定しない場合にエラーになるようにCommandLineライブラリに通知します。これも、実行する必要があるチェックの量を削減するだけです。

フラグの集合としてのオプションの収集

オプションの集合をリストで収集する代わりに、ビットベクトルでenum値の情報を収集することも可能です。cl::bitsクラスで使用される表現は、unsigned整数です。enum値は、enumの序数値のビット位置における0/1で表されます。1はenumが指定されたことを、0はそうでないことを示します。指定された値が解析されるたびに、結果として得られるenumのビットがオプションのビットベクトルに設定されます。

bits |= 1 << (unsigned)enum;

複数回指定されたオプションは冗長です。最初のインスタンス以降は破棄されます。

上記のリストの例を書き直すと、cl::listcl::bitsで置き換えることができます。

cl::bits<Opts> OptimizationBits(cl::desc("Available Optimizations:"),
  cl::values(
    clEnumVal(dce               , "Dead Code Elimination"),
    clEnumVal(instsimplify      , "Instruction Simplification"),
   clEnumValN(inlining, "inline", "Procedure Integration"),
    clEnumVal(strip             , "Strip Symbols")));

instsimplifyが指定されたかどうかをテストするには、cl:bits::isSet関数を使用できます。

if (OptimizationBits.isSet(instsimplify)) {
  ...
}

cl::bits::getBits関数を使用して、生のビットベクトルを取得することも可能です。

unsigned bits = OptimizationBits.getBits();

最後に、外部ストレージを使用する場合は、指定された場所はunsignedである必要があります。それ以外の点では、cl::bitsオプションはcl::listオプションと同等です。

ヘルプ出力へのフリーフォームテキストの追加

プログラムが成長し、成熟するにつれて、その機能に関する要約情報をヘルプ出力に追加することを決定する場合があります。ヘルプ出力は、Unixのmanページと似たようなスタイルで、プログラムに関する簡潔な情報を提供します。しかし、Unixのmanページには、プログラムの機能に関する説明が含まれていることがよくあります。これをCommandLineプログラムに追加するには、main内のcl::ParseCommandLineOptions呼び出しに3番目の引数を渡すだけです。この追加の引数は、プログラムの概要情報として出力され、任意の追加情報を自由に含めることができます。例えば

int main(int argc, char **argv) {
  cl::ParseCommandLineOptions(argc, argv, " CommandLine compiler example\n\n"
                              "  This program blah blah blah...\n");
  ...
}

は、以下のヘルプ出力を生成します。

**OVERVIEW: CommandLine compiler example

  This program blah blah blah...**

USAGE: compiler [options] <input file>

OPTIONS:
  ...
  -help             - display available options (-help-hidden for more)
  -o <filename>     - Specify output filename

オプションのカテゴリへのグループ化

プログラムに多数のオプションがある場合、ツールのユーザーが-helpの出力を操作するのが困難になる可能性があります。この問題を軽減するために、オプションをカテゴリに分類することができます。これは、オプションカテゴリ(cl::OptionCategoryオブジェクト)を宣言し、cl::catオプション属性を使用してこれらのカテゴリにオプションを配置することによって行うことができます。例えば

cl::OptionCategory StageSelectionCat("Stage Selection Options",
                                     "These control which stages are run.");

cl::opt<bool> Preprocessor("E",cl::desc("Run preprocessor stage."),
                           cl::cat(StageSelectionCat));

cl::opt<bool> NoLink("c",cl::desc("Run all stages except linking."),
                     cl::cat(StageSelectionCat));

-helpの出力が、オプションカテゴリが宣言されている場合にカテゴリ分けされます。出力は次のようになります。

OVERVIEW: This is a small program to demo the LLVM CommandLine API
USAGE: Sample [options]

OPTIONS:

  General options:

    -help              - Display available options (-help-hidden for more)
    -help-list         - Display list of available options (-help-list-hidden for more)


  Stage Selection Options:
  These control which stages are run.

    -E                 - Run preprocessor stage.
    -c                 - Run all stages except linking.

オプションカテゴリが宣言されると-helpの動作が変更されることに加えて、コマンドラインオプション-help-listが表示されるようになり、コマンドラインオプションをカテゴリ分けされていないリストとして出力します。

明示的にカテゴリ分けされていないオプションは、cl::getGeneralCategory()カテゴリに配置されることに注意してください。

リファレンスガイド

CommandLineライブラリの基本的な使用方法を理解したので、このセクションでは、コマンドラインオプションの動作を調整するために必要な詳細情報と、より「高度な」コマンドラインオプション処理機能に関する情報を提供します。

位置引数

位置引数は、名前がなく、ハイフンで指定されていない引数です。位置引数は、オプションがその位置だけで指定される場合に使用してください。例えば、標準的なUnixのgrepツールは、正規表現の引数と、検索するオプションのファイル名を取ります(ファイル名が指定されていない場合は標準入力になります)。CommandLineライブラリを使用すると、これは次のように指定します。

cl::opt<string> Regex   (cl::Positional, cl::desc("<regular expression>"), cl::Required);
cl::opt<string> Filename(cl::Positional, cl::desc("<input file>"), cl::init("-"));

これらの2つのオプション宣言を考えると、grepの代替の-help出力は次のようになります。

USAGE: spiffygrep [options] <regular expression> <input file>

OPTIONS:
  -help - display available options (-help-hidden for more)

…そして、結果として得られるプログラムは、標準的なgrepツールと同じように使用できます。

位置引数は、構築順にソートされます。これは、コマンドラインオプションが.cppファイルにリストされている順にソートされますが、位置引数が複数の.cppファイルで定義されている場合、順序は定義されません。この問題の解決策は、すべての位置引数を1つの.cppファイルで定義することだけです。

ハイフンによる位置オプションの指定

ハイフンで始まる値を位置引数に指定する場合があります(たとえば、ファイル内で「-foo」を検索する場合)。最初は、これは困難な作業に見えるかもしれません。なぜなら、システムは「-foo」という名前の引数を検索しようと試み、失敗するからです(シングルクォートでも解決しません)。システムのgrepにも同じ問題があります。

$ spiffygrep '-foo' test.txt
Unknown command line argument '-foo'.  Try: spiffygrep -help'

$ grep '-foo' test.txt
grep: illegal option -- f
grep: illegal option -- o
grep: illegal option -- o
Usage: grep -hblcnsviw pattern file . . .

この問題の解決策は、ツールとシステムの両方で同じです。「--」マーカーを使用します。ユーザーがコマンドラインで「--」を指定すると、プログラムは「--」以降のすべてのオプションをオプションではなく位置引数として扱うように指示します。したがって、次のように使用できます。

$ spiffygrep -- -foo test.txt
  ...output...

getPosition()による絶対位置の決定

場合によっては、オプションが別のオプションの意味に影響を与えたり、変更したりすることがあります。例えば、gcc-x LANGオプションを考えてみましょう。これはgccに、後続の位置引数の接尾辞を無視し、ファイルを言語LANGのソースコードが含まれているかのように解釈するように指示します。これを適切に処理するには、特にリスト内の引数の絶対位置を知る必要があります。これにより、それらの相互作用を正しく適用できます。-llibnameのようなオプション(実際にはダッシュで始まる位置引数)にも役立ちます。

一般的に、問題は、ある程度相互作用する2つのcl::list変数を持っていることです。正しい相互作用を保証するために、cl::list::getPosition(optnum)メソッドを使用できます。このメソッドは、cl::list内のoptnumアイテムのコマンドラインで見つかった絶対位置を返します。

使用のためのイディオムは次のとおりです。

static cl::list<std::string> Files(cl::Positional, cl::OneOrMore);
static cl::list<std::string> Libraries("l");

int main(int argc, char**argv) {
  // ...
  std::vector<std::string>::iterator fileIt = Files.begin();
  std::vector<std::string>::iterator libIt  = Libraries.begin();
  unsigned libPos = 0, filePos = 0;
  while ( 1 ) {
    if ( libIt != Libraries.end() )
      libPos = Libraries.getPosition( libIt - Libraries.begin() );
    else
      libPos = 0;
    if ( fileIt != Files.end() )
      filePos = Files.getPosition( fileIt - Files.begin() );
    else
      filePos = 0;

    if ( filePos != 0 && (libPos == 0 || filePos < libPos) ) {
      // Source File Is next
      ++fileIt;
    }
    else if ( libPos != 0 && (filePos == 0 || libPos < filePos) ) {
      // Library is next
      ++libIt;
    }
    else
      break; // we're done with the list
  }
}

互換性の理由から、cl::optunsigned getPosition()オプションをサポートしており、そのオプションの絶対位置を提供します。2つのリストの場合と同様に、cl::optcl::listオプションを使用して上記と同じアプローチを適用できます。

cl::ConsumeAfter修飾子

cl::ConsumeAfterフォーマットオプションは、「インタープリタースタイル」のオプション処理を使用するプログラムを作成するために使用されます。このスタイルのオプション処理では、最後の位置引数以降に指定されたすべての引数は、コマンドライン引数によって解釈されない特別なインタープリター引数として扱われます。

具体的な例として、標準的なUnix Bourneシェル(/bin/sh)の代替を開発しているとしましょう。/bin/shを実行するには、最初にシェル自体へのオプション(トレース出力をオンにする-xなど)を指定し、次に実行するスクリプトの名前を指定し、次にスクリプトへの引数を指定します。スクリプトへのこれらの引数は、Bourneシェルのコマンドラインオプションプロセッサによって解析されますが、シェル自体のオプションとしては解釈されません。CommandLineライブラリを使用すると、これは次のように指定します。

cl::opt<string> Script(cl::Positional, cl::desc("<input script>"), cl::init("-"));
cl::list<string>  Argv(cl::ConsumeAfter, cl::desc("<program arguments>..."));
cl::opt<bool>    Trace("x", cl::desc("Enable trace output"));

これにより、ヘルプ出力が自動的に提供されます。

USAGE: spiffysh [options] <input script> <program arguments>...

OPTIONS:
  -help - display available options (-help-hidden for more)
  -x    - Enable trace output

実行時に、新しいシェル代替を`spiffysh -x test.sh -a -x -y bar’として実行すると、Trace変数はtrueに設定され、Script変数は「test.sh」に設定され、Argvリストには["-a", "-x", "-y", "bar"]が含まれます。これは、最後の位置引数(スクリプト名)の後に指定されたためです。

cl::ConsumeAfterオプションを指定できる場合にいくつかの制限があります。例えば、プログラムごとに1つのcl::ConsumeAfterしか指定できず、少なくとも1つの位置引数を指定する必要があり、cl::listの位置引数は存在せず、cl::ConsumeAfterオプションはcl::listオプションである必要があります。

内部ストレージと外部ストレージ

デフォルトでは、すべてのコマンドラインオプションは、コマンドラインから解析した値を自動的に保持します。これは一般的なケースでは非常に便利です。特に、使用するファイルでコマンドラインオプションを定義できる機能と組み合わせると便利です。これは、内部ストレージモデルと呼ばれます。

しかしながら、コマンドラインオプションの処理コードと、解析された値の格納を分離することは、時に便利です。例えば、「-debug」オプションを使用して、プログラム全体でデバッグ情報を有効にしたいとします。この場合、デバッグコードを制御するブール値はグローバルにアクセス可能である必要があります(例えば、ヘッダーファイル内)。しかし、コマンドラインオプションの処理コードは、これらのすべてのクライアントに公開されるべきではありません(多くの.cppファイルで#include CommandLine.hする必要が生じるため)。

これを行うには、次のようにオプションを指定して.hファイルをセットアップします。

// DebugFlag.h - Get access to the '-debug' command line option
//

// DebugFlag - This boolean is set to true if the '-debug' command line option
// is specified.  This should probably not be referenced directly, instead, use
// the DEBUG macro below.
//
extern bool DebugFlag;

// DEBUG macro - This macro should be used by code to emit debug information.
// In the '-debug' option is specified on the command line, and if this is a
// debug build, then the code specified as the option to the macro will be
// executed.  Otherwise it will not be.
#ifdef NDEBUG
#define LLVM_DEBUG(X)
#else
#define LLVM_DEBUG(X) do { if (DebugFlag) { X; } } while (0)
#endif

これにより、クライアントはLLVM_DEBUG()マクロ、または必要に応じてDebugFlagを自由に使用できます。これで、オプションが設定されたときにDebugFlagブール値を設定するだけで済みます。これを行うには、コマンドライン引数プロセッサに追加の引数を渡します。そして、cl::location属性を使用してどこに値を格納するかを指定します。

bool DebugFlag;                  // the actual value
static cl::opt<bool, true>       // The parser
Debug("debug", cl::desc("Enable debug output"), cl::Hidden, cl::location(DebugFlag));

上記の例では、cl::optテンプレートの第2引数として「true」を指定しています。これは、テンプレート自体が値のコピーを保持しないことを示しています。これに加えて、cl::location属性を指定することで、DebugFlagが自動的に設定されます。

オプション属性

このセクションでは、オプションに指定できる基本的な属性について説明します。

  • オプション名属性(位置指定オプションを除くすべてのオプションで必須)は、オプションの名前を指定します。このオプションは、単純な二重引用符で指定します。

    cl::opt<bool> Quiet("quiet");
    
  • cl::desc属性は、プログラムの-help出力に表示されるオプションの説明を指定します。この属性は、改行文字「n」で区切られた複数行の説明をサポートします。

  • cl::value_desc属性は、コマンドラインオプションの-help出力を微調整するために使用できる文字列を指定します。例についてはこちらを参照してください。

  • cl::init属性は、スカラーオプションの初期値を指定します。この属性が指定されていない場合、コマンドラインオプションの値は、その型のデフォルトコンストラクタによって作成された値になります。

    警告

    オプションにcl::initcl::locationの両方を指定する場合、コマンドラインパーサーがcl::initを認識できるように、cl::locationを先に指定する必要があります。(正しい順序で指定しないと、実行時にエラーが発生します。)

  • 外部ストレージを使用する場合、cl::location属性は、解析されたコマンドラインオプションの値を格納する場所を指定します。詳細については、内部ストレージと外部ストレージのセクションを参照してください。

  • cl::aliasopt属性は、cl::aliasオプションがどのオプションのエイリアスであるかを指定します。

  • cl::values属性は、汎用パーサーで使用される文字列から値へのマッピングを指定します。オプション名、マッピングされる値、ツールで-helpに表示される説明を指定する(オプション、値、説明)の3つ組のリストを取ります。汎用パーサーは列挙値で最も頻繁に使用されるため、2つのマクロが役立つことがよくあります。

    1. clEnumValマクロは、列挙型の3つ組を簡単に指定するための方法として使用されます。このマクロは、オプション名を列挙名と同じにするように自動的にします。マクロへの最初のオプションは列挙型、2番目はコマンドラインオプションの説明です。

    2. clEnumValNマクロは、オプション名と列挙名が異なるマクロオプションを指定するために使用されます。このマクロでは、最初の引数は列挙値、2番目はフラグ名、3番目は説明です。

    サポートしていないパーサーでcl::valuesを使用しようとすると、コンパイル時エラーが発生します。

  • cl::multi_val属性は、このオプションが複数の値を持つことを指定します(例:-sectalign segname sectname sectvalue)。この属性は、オプションの値の数である符号なしの引数を1つ取ります。この属性はcl::listオプションでのみ有効です(他のオプションタイプで使用しようとすると、コンパイルエラーが発生します)。(もちろんcl::ValueDisallowed以外)マルチ値オプションでは、通常の修飾子をすべて使用できます。

  • cl::cat属性は、オプションが属するオプションカテゴリを指定します。cl::OptionCategoryオブジェクトである必要があります。

  • cl::callback属性は、オプションが表示されたときに呼び出されるコールバック関数を指定し、オプションBがオプションAを意味する場合など、他のオプションを設定するために使用できます。オプションがcl::listであり、cl::CommaSeparatedも指定されている場合、コールバックは値ごとに1回実行されます。これは、組み合わせを検証したり、他のオプションを選択的に設定したりするために使用できます。

    cl::opt<bool> OptA("a", cl::desc("option a"));
    cl::opt<bool> OptB(
        "b", cl::desc("option b -- This option turns on option a"),
        cl::callback([&](const bool &) { OptA = true; }));
    cl::list<std::string, cl::list<std::string>> List(
      "list",
      cl::desc("option list -- This option turns on options a when "
               "'foo' is included in list"),
      cl::CommaSeparated,
      cl::callback([&](const std::string &Str) {
        if (Str == "foo")
          OptA = true;
      }));
    

オプション修飾子

オプション修飾子とは、cl::optcl::listのコンストラクタに渡すフラグと式です。これらの修飾子を使用すると、オプションの解析方法と-help出力の生成方法を調整して、アプリケーションに最適な状態にすることができます。

これらのオプションは、5つの主要なカテゴリに分類されます。

  1. -help 出力からオプションを隠す

  2. 必要な回数と許容回数の制御

  3. 値の指定の必要性の制御

  4. その他の書式設定オプションの制御

  5. その他オプション修飾子

雑多なカテゴリのオプションを除き、同じカテゴリの2つのオプションを1つのオプションに指定することはできません(実行時エラーが発生します)。CommandLineライブラリは、実際にもっとも役立ち、もっとも一般的な設定のデフォルト値を指定するため、通常はこれらを気にする必要はありません。

-help出力からオプションを非表示にする

cl::NotHiddencl::Hidden、およびcl::ReallyHidden修飾子は、コンパイルされたプログラムの-helpおよび-help-hidden出力にオプションが表示されるかどうかを制御するために使用されます。

  • cl::NotHidden修飾子(cl::optおよびcl::listオプションのデフォルト)は、オプションが両方のヘルプ一覧に表示されることを示します。

  • cl::Hidden修飾子(cl::aliasオプションのデフォルト)は、オプションが-help出力に表示されないが、-help-hidden出力には表示されることを示します。

  • cl::ReallyHidden修飾子は、オプションがヘルプ出力に表示されないことを示します。

必要な回数と許可される回数を制御する

このオプショングループは、プログラムのコマンドラインでオプションを指定できる(または必要な)回数を制御するために使用されます。この設定の値を指定すると、CommandLineライブラリでエラーチェックを行うことができます。

このオプショングループで使用できる値は次のとおりです。

  • cl::Optional修飾子(cl::optおよびcl::aliasクラスのデフォルト)は、プログラムでオプションを0回または1回指定できることを示します。

  • cl::ZeroOrMore修飾子(cl::listクラスのデフォルト)は、プログラムでオプションを0回以上指定できることを示します。

  • cl::Required修飾子は、指定されたオプションを正確に1回指定する必要があることを示します。

  • cl::OneOrMore修飾子は、オプションを少なくとも1回指定する必要があることを示します。

  • cl::ConsumeAfter修飾子については、位置指定引数のセクションで説明されています。

オプションが指定されていない場合、オプションの値はcl::init属性で指定された値になります。cl::init属性が指定されていない場合、オプションの値はデータ型のデフォルトコンストラクタで初期化されます。

cl::optクラスのオプションに対してオプションが複数回指定されている場合、最後の値のみが保持されます。

値を指定する必要があるかどうかを制御する

このオプション群は、オプションが値を持つことを許可するかどうかを制御するために使用されます。CommandLineライブラリの場合、値は等号付きで指定されるか(例:`-index-depth=17`)、末尾の文字列として指定されます(例:`-o a.out`)。

このオプショングループで使用できる値は次のとおりです。

  • **cl::ValueOptional** 修飾子(bool 型のオプションのデフォルト)は、値があってもなくても受け入れられることを指定します。ブール型の引数はコマンドラインに表示されるだけで有効になる場合もあれば、明示的に `-foo=true` と指定する場合もあります。このモードでオプションが指定されている場合、等号なしで値を指定することは不正です。したがって、`-foo true` は不正です。この動作を得るには、cl::ValueRequired 修飾子を使用する必要があります。

  • **cl::ValueRequired** 修飾子(汎用パーサーを使用した名前のない代替案を除くすべての型のデフォルト)は、値を必ず指定する必要があることを指定します。このモードは、コマンドラインライブラリに、オプションに等号が付加されていない場合、次に指定された引数が値である必要があることを通知します。これにより、`-o a.out` のような動作が可能になります。

  • **cl::ValueDisallowed** 修飾子(汎用パーサーを使用した名前のない代替案のデフォルト)は、ユーザーが値を指定することがランタイムエラーであることを示します。これは、ユーザーがブール値のオプションに値を指定することを禁止するために使用できます(例:`-foo=true`)。

一般的に、このオプショングループのデフォルト値は、期待どおりに動作します。前述のように、cl::ValueDisallowed 修飾子をブール型の引数に指定して、コマンドラインパーサーを制限することができます。これらのオプションは、主にライブラリの拡張時に役立ちます。

その他の書式設定オプションの制御

書式設定オプショングループは、コマンドラインオプションが特別な機能を持ち、他のコマンドライン引数とは異なることを指定するために使用されます。通常どおり、これらの引数は最大1つしか指定できません。

  • **cl::NormalFormatting** 修飾子(すべてのオプションのデフォルト)は、このオプションが「標準」であることを指定します。

  • **cl::Positional** 修飾子は、コマンドラインオプションが関連付けられていない位置引数であることを指定します。詳細については、位置引数セクションを参照してください。

  • **cl::ConsumeAfter** 修飾子は、このオプションが「インタプリタスタイル」の引数をキャプチャするために使用されることを指定します。このセクションで詳細情報をご覧ください。

  • **cl::Prefix** 修飾子は、このオプションがその値に接頭辞を付けることを指定します。「Prefix」オプションでは、等号はオプション名から値を分離しません。代わりに、値は接頭辞の後のすべてであり、存在する場合は等号も含みます。これは、リンカツールでの `-lmalloc` や `-L/usr/lib`、コンパイラツールでの `-DNAME=value` のような特殊な引数を処理する場合に役立ちます。ここで、`l`、`D`、`L` オプションは通常の文字列(またはリスト)オプションであり、CommandLineライブラリがそれらを認識できるように**cl::Prefix** 修飾子が追加されています。**cl::Prefix** オプションには、**cl::ValueDisallowed** 修飾子を指定してはならないことに注意してください。

オプションのグループ化の制御

**cl::Grouping** 修飾子は、cl::Positionalを除くすべての書式設定タイプと組み合わせることができます。これは、多くの単一文字の引数を持つが、単一のダッシュしか必要としないUnixスタイルのツール(`ls` など)を実装するために使用されます。たとえば、`ls -labF` コマンドは実際には4つの異なるオプションを有効にします。これらはすべて単一文字です。

**cl::Grouping** オプションは、個別に使用される場合、またはグループの最後に使用される場合にのみ値を持つことができることに注意してください。cl::ValueRequiredの場合、このようなオプションがグループ内の他の場所で使用されるとランタイムエラーになります。

CommandLineライブラリは、**cl::Prefix**または**cl::Grouping**修飾子の使用方法を制限しませんが、あいまいな引数設定を指定することが可能です。したがって、プレフィックスまたはグループ化オプションである複数の文字のオプションを持つことが可能であり、それらは設計どおりに機能します。

これを行うために、CommandLineライブラリは貪欲アルゴリズムを使用して入力オプションを(複数の可能性のある)プレフィックスとグループ化オプションに解析します。その戦略は基本的に次のようになります。

parse(string OrigInput) {

1. string Input = OrigInput;
2. if (isOption(Input)) return getOption(Input).parse();  // Normal option
3. while (!Input.empty() && !isOption(Input)) Input.pop_back();  // Remove the last letter
4. while (!Input.empty()) {
     string MaybeValue = OrigInput.substr(Input.length())
     if (getOption(Input).isPrefix())
       return getOption(Input).parse(MaybeValue)
     if (!MaybeValue.empty() && MaybeValue[0] == '=')
       return getOption(Input).parse(MaybeValue.substr(1))
     if (!getOption(Input).isGrouping())
       return error()
     getOption(Input).parse()
     Input = OrigInput = MaybeValue
     while (!Input.empty() && !isOption(Input)) Input.pop_back();
     if (!Input.empty() && !getOption(Input).isGrouping())
       return error()
   }
5. if (!OrigInput.empty()) error();

}

その他オプション修飾子

その他オプション修飾子は、セットから複数のフラグを指定できる唯一のフラグです。これらは相互に排他的ではありません。これらのフラグは、オプションを変更するブール値のプロパティを指定します。

  • **cl::CommaSeparated** 修飾子は、オプションの値に指定されたコンマを使用して、値をオプションの複数の値に分割することを示します。たとえば、cl::CommaSeparatedが指定されている場合、これらの2つのオプションは同等です。「-foo=a -foo=b -foo=c」と「-foo=a,b,c」。このオプションは、オプションが1つ以上の値を受け入れることが許可されている場合(つまり、cl::listオプションである場合)にのみ意味があります。

  • **cl::DefaultOption** 修飾子は、オプションがアプリケーション固有のパーサーによって上書きできるデフォルトであることを指定するために使用されます。たとえば、`-help` のエイリアス `-h` はこのように登録されているため、`-h` オプションを別の目的(通常のオプションまたは別のオプションのエイリアスとして)で使用しなければならないアプリケーションによって上書きできます。

  • **cl::PositionalEatsArgs** 修飾子(位置引数にのみ適用され、リストにのみ意味があります)は、位置引数がその後の文字列(「-」で始まる文字列を含む)を、別の認識された位置引数まで消費することを示します。たとえば、2つの「消費する」位置引数「pos1」と「pos2」がある場合、「-pos1 -foo -bar baz -pos2 -bork」という文字列は、「-foo -bar baz」文字列を「-pos1」オプションに、「-bork」文字列を「-pos2」オプションに適用します。

  • **cl::Sink** 修飾子は、不明なオプションを処理するために使用されます。cl::Sink修飾子が指定されたオプションが少なくとも1つある場合、パーサーはエラーを通知する代わりに、認識されないオプション文字列を値として渡します。cl::CommaSeparatedと同様に、この修飾子はcl::listオプションの場合にのみ意味があります。

レスポンスファイル

Microsoft Windowsの一部のバリアントや古いUnixなどの一部のシステムでは、コマンドラインの長さに比較的低い制限があります。そのため、この制限を回避するために、いわゆる「レスポンスファイル」を使用するのが一般的です。これらのファイルはコマンドライン(「@file」構文を使用)で指定されます。プログラムはこれらのファイルを読み取り、その内容をargvに挿入することにより、コマンドラインの長さの制限を回避します。

トップレベルのクラスと関数

すべての組み込みの柔軟性にもかかわらず、CommandLineオプションライブラリは、実際にはcl::ParseCommandLineOptions関数と、cl::optcl::listcl::aliasの3つの主要なクラスのみで構成されています。このセクションでは、これらの3つのクラスについて詳しく説明します。

cl::getRegisteredOptions関数

cl::getRegisteredOptions関数は、宣言された非位置引数のコマンドラインオプションにプログラマーがアクセスできるように設計されており、cl::ParseCommandLineOptionsを呼び出す前に、-helpにどのように表示されるかを変更できます。このメソッドは、すべてのオプションが初期化されるとは限らないため、静的初期化中に呼び出してはならないことに注意してください。したがって、mainから呼び出す必要があります。

この関数は、ツール作成者が直接アクセスできない可能性のあるライブラリに宣言されたオプションにアクセスするために使用できます。

この関数は、オプション文字列(例:-help)をOption*にマッピングするStringMapを取得します。

関数の使用方法の例を次に示します。

using namespace llvm;
int main(int argc, char **argv) {
  cl::OptionCategory AnotherCategory("Some options");

  StringMap<cl::Option*> &Map = cl::getRegisteredOptions();

  //Unhide useful option and put it in a different category
  assert(Map.count("print-all-options") > 0);
  Map["print-all-options"]->setHiddenFlag(cl::NotHidden);
  Map["print-all-options"]->setCategory(AnotherCategory);

  //Hide an option we don't want to see
  assert(Map.count("enable-no-infs-fp-math") > 0);
  Map["enable-no-infs-fp-math"]->setHiddenFlag(cl::Hidden);

  //Change --version to --show-version
  assert(Map.count("version") > 0);
  Map["version"]->setArgStr("show-version");

  //Change --help description
  assert(Map.count("help") > 0);
  Map["help"]->setDescription("Shows help");

  cl::ParseCommandLineOptions(argc, argv, "This is a small program to demo the LLVM CommandLine API");
  ...
}

cl::ParseCommandLineOptions関数

cl::ParseCommandLineOptions関数はmainから直接呼び出すように設計されており、argcargvが使用可能になった後、すべてのコマンドラインオプション変数の値を入力するために使用されます。

cl::ParseCommandLineOptions関数は2つのパラメーター(argcargv)を必要としますが、-helpオプションが呼び出されたときに出力する追加のテキストを保持するオプションの第3パラメーターも取ることができます。

cl::SetVersionPrinter関数

cl::SetVersionPrinter関数は、main関数から直接、かつcl::ParseCommandLineOptions関数を呼び出す*前*に呼び出すように設計されています。これはオプション機能です。--versionオプションへの応答として関数を呼び出すように設定するだけで、CommandLineライブラリがLLVMの通常のバージョン文字列を出力することを回避します。これは、LLVMの一部ではないがCommandLine機能を使用したいプログラムにとって便利です。このようなプログラムは、引数を取得せずvoidを返す小さな関数を定義し、その中でプログラムに適切なバージョン情報を表示する必要があります。その関数のアドレスをcl::SetVersionPrinterに渡すと、ユーザーが--versionオプションを指定したときにその関数が呼び出されるようになります。

cl::optクラス

cl::optクラスは、スカラーコマンドラインオプションを表すために使用されるクラスであり、最も頻繁に使用されます。これはテンプレートクラスであり、最大3つの引数を取ることができます(最初の引数以外はデフォルト値を持っています)。

namespace cl {
  template <class DataType, bool ExternalStorage = false,
            class ParserClass = parser<DataType> >
  class opt;
}

最初のテンプレート引数は、コマンドライン引数の基になるデータ型を指定し、デフォルトのパーサー実装を選択するために使用されます。2番目のテンプレート引数は、オプションのストレージをオプションに含めるべきか(デフォルト)、またはオプションのために解析された値を格納するために外部ストレージを使用するべきかを指定するために使用されます(詳細は内部ストレージと外部ストレージを参照してください)。

3番目のテンプレート引数は、使用するパーサーを指定します。デフォルト値は、オプションの基になるデータ型に基づいてparserクラスのインスタンスを選択します。一般的に、このデフォルト値はほとんどのアプリケーションでうまく機能するため、このオプションはカスタムパーサーを使用する場合にのみ使用されます。

cl::listクラス

cl::listクラスは、コマンドラインオプションのリストを表すために使用されるクラスです。これもテンプレートクラスであり、最大3つの引数を取ることができます。

namespace cl {
  template <class DataType, class Storage = bool,
            class ParserClass = parser<DataType> >
  class list;
}

このクラスはcl::optクラスと全く同じように動作しますが、2番目の引数はブール値ではなく、外部ストレージの**型**です。このクラスでは、マーカー型'bool'が内部ストレージを使用することを示すために使用されます。

cl::bitsクラス

cl::bitsクラスは、ビットベクター形式のコマンドラインオプションのリストを表すために使用されるクラスです。これもテンプレートクラスであり、最大3つの引数を取ることができます。

namespace cl {
  template <class DataType, class Storage = bool,
            class ParserClass = parser<DataType> >
  class bits;
}

このクラスはcl::listクラスと全く同じように動作しますが、外部ストレージを使用する場合は、2番目の引数は**型**'unsigned'でなければなりません。

cl::aliasクラス

cl::aliasクラスは、他の引数のエイリアスを作成するために使用される非テンプレートクラスです。

namespace cl {
  class alias;
}

cl::aliasopt属性を使用して、これがどのオプションのエイリアスであるかを指定する必要があります。エイリアス引数はデフォルトでcl::Hiddenになり、エイリアスされたオプションのパーサーを使用して文字列からデータへの変換を行います。

cl::extrahelpクラス

cl::extrahelpクラスは、-helpオプションに対して追加のヘルプテキストを出力できるようにする非テンプレートクラスです。

namespace cl {
  struct extrahelp;
}

extrahelpを使用するには、コンストラクタにconst char*パラメータを持つインスタンスを作成します。コンストラクタに渡されたテキストは、ヘルプメッセージの最後にそのまま出力されます。複数のcl::extrahelpを**使用できます**が、これは推奨されません。ツールに追加のヘルプ情報を表示する必要がある場合は、そのヘルプ情報を1つのcl::extrahelpインスタンスにまとめてください。

例:

cl::extrahelp("\nADDITIONAL HELP:\n\n  This is the extra help\n");

cl::OptionCategoryクラス

cl::OptionCategoryクラスは、オプションカテゴリを宣言するためのシンプルなクラスです。

namespace cl {
  class OptionCategory;
}

オプションカテゴリには、名前とオプションで説明が必要で、const char*としてコンストラクタに渡されます。

オプションの解析前(例:静的に)にオプションカテゴリを宣言し、オプションに関連付けることで、-helpの出力がカテゴリ化されていない状態からカテゴリ化された状態に変更されます。オプションカテゴリが宣言されているが、オプションに関連付けられていない場合は、-helpの出力から非表示になります。

組み込みパーサー

パーサーは、コマンドラインから取得された文字列値を、C++プログラムで使用できる型付き値に変換する方法を制御します。デフォルトでは、CommandLineライブラリは、コマンドラインオプションが型 ' type ' の値を使用することを指定している場合、parser<type> のインスタンスを使用します。このため、カスタムオプション処理は ' parser ' クラスの特殊化を使用して指定されます。

CommandLineライブラリは、次の組み込みパーサー特殊化を提供しており、ほとんどのアプリケーションで十分です。ただし、新しいデータ型や同じデータの解釈方法で機能を拡張することもできます。この種のライブラリエクステンションの詳細については、カスタムパーサーの作成を参照してください。

  • 汎用parser<t>パーサーは、cl::valuesプロパティを使用してマッピング情報を指定することで、文字列値を任意のデータ型にマップするために使用できます。このパーサーの最も一般的な用途は列挙値の解析であり、有効な列挙値のみが指定されていることを確認するためのすべてのエラーチェックにCommandLineライブラリを使用できます(任意の文字列を受け入れるのではなく)。ただし、この汎用パーサークラスは任意のデータ型に使用できます。

  • **parser<bool>特殊化**は、ブール文字列をブール値に変換するために使用されます。現在受け入れられている文字列は、「true」、「TRUE」、「True」、「1」、「false」、「FALSE」、「False」、および「0」です。

  • **parser<boolOrDefault>特殊化**は、値がブール値であるものの、オプションが実際に指定されたかどうかを知る必要がある場合に使用されます。boolOrDefaultは、BOU_UNSET、BOU_TRUE、BOU_FALSEの3つの値を持つ列挙型です。このパーサーは、**`parser<bool>`**と同じ文字列を受け入れます。

  • **parser<string>特殊化**は、解析された文字列を指定された文字列値に格納するだけです。データの変換や変更は行われません。

  • **parser<int>特殊化**は、Cのstrtol関数を使用して文字列入力を解析します。そのため、(オプションの ' + ' または ' - ' プレフィックス付きの)10進数を(0以外の数字で始まる必要がある)受け入れます。' 0 'プレフィックス数字で識別される8進数と、' 0x 'または' 0X 'プレフィックスを持つ16進数を受け入れます。

  • **parser<double>**と**parser<float>特殊化**は、標準Cのstrtod関数を使用して浮動小数点文字列を浮動小数点値に変換します。そのため、指数表記(例:1.7e15)を含む幅広い文字列形式がサポートされ、ロケールも適切にサポートされます。

拡張ガイド

CommandLineライブラリには既に多くの機能が組み込まれています(前述のとおり)が、その真の強みは拡張性にあります。このセクションでは、CommandLineライブラリが内部でどのように機能するかを説明し、いくつかの単純で一般的な拡張方法を示します。

カスタムパーサーの作成

最も単純で一般的な拡張の1つは、カスタムパーサーの使用です。前述のとおり、パーサーは、CommandLineライブラリの中で、ユーザーからの文字列入力を特定の解析済みデータ型に変換し、その過程で入力を検証する部分です。

新しいパーサーを使用するには、2つの方法があります。

  1. カスタムデータ型に対してcl::parserテンプレートを特殊化します。

    このアプローチの利点は、カスタムデータ型を使用するユーザーは、データ型の値を持つオプションを定義するたびに、自動的にカスタムパーサーを使用することです。このアプローチの欠点は、基本的なデータ型が既にサポートされている場合は機能しないことです。

  2. 独立したクラスを作成し、それを必要とするオプションから明示的に使用します。

    このアプローチは、あまり特殊ではないデータ型に対して特別な構文を使用してオプションを解析したい状況で効果を発揮します。このアプローチの欠点は、パーサーのユーザーが、ビルトインのパーサーではなく、独自のパーサーを使用していることを認識する必要があることです。

議論を分かりやすくするために、数値サイズにオプションの単位を付けたファイルサイズを受け付けるカスタムパーサーについて説明します。例えば、「102kb」、「41M」、「1G」を適切な整数値に解析することを目指します。この場合、解析先の基となるデータ型は`unsigned`です。すべての`unsigned`オプションのデフォルトにしないため、上記のアプローチ#2を選択します。

まず、新しい`FileSizeParser`クラスを宣言します。

struct FileSizeParser : public cl::parser<unsigned> {
  // parse - Return true on error.
  bool parse(cl::Option &O, StringRef ArgName, const std::string &ArgValue,
             unsigned &Val);
};

新しいクラスは`cl::parser`テンプレートクラスを継承して、デフォルトの定型コードを埋め込みます。解析先のデータ型を指定します。これは`parse`メソッドの最後の引数であり、カスタムパーサーのクライアントが`parse`メソッドに渡すオブジェクト型を認識できるようにするためです。(ここでは、`unsigned`変数に解析することを宣言します。)

ほとんどの場合、カスタムパーサーで実装する必要がある唯一のメソッドは`parse`メソッドです。`parse`メソッドは、オプションが呼び出されるたびに呼び出され、オプション自体、オプション名、解析する文字列、戻り値への参照が渡されます。解析する文字列が正しくフォーマットされていない場合、パーサーはエラーメッセージを出力し、trueを返す必要があります。それ以外の場合は、falseを返し、`Val`に解析された値を設定する必要があります。この例では、`parse`を次のように実装します。

bool FileSizeParser::parse(cl::Option &O, StringRef ArgName,
                           const std::string &Arg, unsigned &Val) {
  const char *ArgStart = Arg.c_str();
  char *End;

  // Parse integer part, leaving 'End' pointing to the first non-integer char
  Val = (unsigned)strtol(ArgStart, &End, 0);

  while (1) {
    switch (*End++) {
    case 0: return false;   // No error
    case 'i':               // Ignore the 'i' in KiB if people use that
    case 'b': case 'B':     // Ignore B suffix
      break;

    case 'g': case 'G': Val *= 1024*1024*1024; break;
    case 'm': case 'M': Val *= 1024*1024;      break;
    case 'k': case 'K': Val *= 1024;           break;

    default:
      // Print an error message if unrecognized character!
      return O.error("'" + Arg + "' value invalid for file size argument!");
    }
  }
}

この関数は、対象とする文字列の種類のための非常に単純なパーサーを実装しています。いくつかの欠点があります(例えば「`123KKK`」を許可します)が、この例としては十分です。エラーメッセージを得るために(`error`メソッドは常にtrueを返します)、オプション自体を使用してエラーメッセージを出力することに注意してください(下記に示します)。パーサークラスができたので、次のように使用できます。

static cl::opt<unsigned, false, FileSizeParser>
MFS("max-file-size", cl::desc("Maximum file size to accept"),
    cl::value_desc("size"));

これにより、プログラムの出力に以下が追加されます。

OPTIONS:
  -help                 - display available options (-help-hidden for more)
  ...
  -max-file-size=<size> - Maximum file size to accept

そして、解析が正しく機能することをテストできます(テストプログラムは`max-file-size`引数の値を出力するだけです)。

$ ./test
MFS: 0
$ ./test -max-file-size=123MB
MFS: 128974848
$ ./test -max-file-size=3G
MFS: 3221225472
$ ./test -max-file-size=dog
-max-file-size option: 'dog' value invalid for file size argument!

正しく動作しているようです。エラーメッセージは分かりやすく、妥当なファイルサイズを受け入れているようです。「カスタムパーサー」チュートリアルのまとめです。

外部ストレージの活用

いくつかのLLVMライブラリは、そのライブラリとリンクするすべてのプログラムに自動的に含まれる静的な`cl::opt`インスタンスを定義しています。これは機能です。しかし、場合によっては、ライブラリの外部でコマンドラインオプションの値を知る必要があることがあります。このような場合、ライブラリは、ライブラリのユーザーがアクセスできる外部ストレージの場所を提供する必要があります、またはするべきです。その例としては、`lib/Support/Debug.cpp`ファイルによってエクスポートされる`llvm::DebugFlag`や、`lib/IR/PassManager.cpp`ファイルによってエクスポートされる`llvm::TimePassesIsEnabled`フラグなどがあります。

コマンドラインオプションの動的な追加