コマンドライン引数を簡単に取り扱うクラス

コマンドライン引数を取り扱うとき getopt や Boost::program_options 以外に、google-gflags というのを教えてもらった。
http://googlejapan.blogspot.com/2007/07/google-1.html

getopt も Boost::program_options もオプションのリストをあらかじめ作る必要があるが、google-gflags では必要になったら追加する、といった記述ができる。LL 感覚でプログラムを書くときは便利そうだと思い、自分でも遊びで作ってみた。

目標

OPTION_DEFINE(g_numOfData, 123, "num-of-data", "Number of data");
  • ↑のようにマクロ感覚で1行で変数、初期値、オプション引数のシンボル、説明文が定義できる
  • あとはどこかでうまく処理してくれる
    • 実行時引数に --num-of-data 1234 などと指定したら、g_numOfData に代入される
    • 実行時引数がなければ初期値が、g_numOfData に代入される
    • パースエラー時は定義されているオプションの一覧を表示する

定義を書くと自動的に static なリストに追加されるようにしたいが、リストに追加するコードをどうやって駆動させるか考えてみた。思いついたのは次の2つ。

  • グローバルなクラスインスタンスならば、コンストラクタで処理できる
  • グローバル変数を関数で初期化すれば自動で呼び出されて処理できる
  • 他は分からず・・・よい方法があれば教えてください。

コンストラクタを利用したデザイン

http://github.com/iwagaki/sandbox/tree/master/OptionParser/

OPTION_INT    g_numOfData("num-of-data", "Number of data", 123);

int main(int argc, char** argv)
{
    OptionParser::parse(argc, argv);

    printf("g_numOfData = %d\n", (int)g_numOfData);

    return 0;
}
  • OPTION_INT というクラスを作り、コンストラクタで static std::vector<...> OptionParser::m_optionList に登録していく
  • main() で parse() を呼ぶと m_optionList に従って解析して、各オブジェクトにオプションの引数を代入する
  • 利用する側はオブジェクトに対してアクセスする
    • operator T() を定義してあるので、ある程度は通常の型と同じ感覚で読み出せる

難点は、

  • OPTION_INT など特別な型を使うのが気持ち悪い
  • 暗黙の型変換による読み出しがトリッキーで気持ち悪い

グローバル変数の関数による初期化を利用したデザイン

http://github.com/iwagaki/sandbox/tree/master/OptionParser2/

int& g_numOfData = OptionParser::createOption(123, "num-of-data", "Number of data");

int main(int argc, char** argv)
{
    OptionParser::parse(argc, argv);

    printf("g_numOfData = %d\n", g_numOfData);

    return 0;
}
  • 普通の型で宣言をして、専用関数で初期化することで、static std::vector<...> OptionParser::m_optionList に登録していく
  • main() で parse() を呼ぶと m_optionList に従って解析して、各変数の実態に引数を代入する
  • 利用する側は普通に変数にアクセスする

難点は、

  • 変数の実体とのヒモ付けをするために参照を利用していて気持ち悪い
  • C言語と違い、C++ ではグローバル変数を関数の戻り値で初期化することができるようだが気持ち悪い

結論

作るのは楽しかったが、他人と共有するコードでは Boost::program_options を使う。