コマンドラインパーサを作ってみました



コマンドラインパーサ(コマンドラインオプション解析器)

拙作ツールをコマンドラインオプション対応にしようと考えて、 既存のものをいろいろ探してみたのですが、私にとっては 帯に短したすきに長しで、しっくりくるものが無かったため、 自分で作ってしまいました。

https://github.com/trueroad/cmdlineparse

C++11 用で、ヘッダファイルひとつだけでできていますので、 インストール不要、簡単に使えます。 ご自分のプロジェクトへ cmdlineparse.hh をコピーして、 ドキュメント を参考にパーサを呼び出す処理を書いていただければ OK です。 ライセンスは 2 条項 BSD にしましたので、使っていただきやすいと思います。 また、GPL の方が良いという方向けに、 GPL 版も用意しています。

各種コマンドラインパーサ

最初は自分で実装するつもりはなくて、いくつか探してみました。 C++コマンドライン引数 は、非常に参考になりました。私の場合の条件としては、

あたりになります。

POSIX 標準

getopt ()

POSIX 標準だと getopt () があるのですが、 これだと長いオプションが使えません。

GNU 系

getopt_long ()

getopt () を、長いオプションを使えるように拡張したものが、 getopt_long () です。 基本的な解析の機能そのものは十分ですが C 言語ベースになります。 また、これには低レイヤの機能しかありませんし、 ヘルプメッセージの作成機能もありません。

gengetopt

getopt_long () を解析エンジンとして使い、 高レイヤの機能を持ちヘルプメッセージも作成できる、 gengetopt があります。 面白いのは、あらかじめオプションやヘルプメッセージなどの定義ファイルを作り、 それを gengetopt で処理すると、 C 言語で書かれたコマンドラインパーサが生成される、 という構造になっているところです。 これを使うか、getopt_long () をそのまま使うか、 とも考えたのですが、結局 C 言語ベースになってしまうので、 C++ 的にはどうしても使いにくい、わかりにくい、という感触があります。 C++ が使えずに C で書かざるを得ない時には、最有力になると思います。

Argp

同じく GNU 系だと Argp というコマンドラインパーサがあって、 getopt_long () よりも高機能、ということのようですが、残念ながらこれも C 言語になります。

C++

boost::program_options

C++ ならこれが本命、かもしれない boost の boost::program_options です。 非常に良い、とは思うのですが、インストールしないと使えません。 非開発者向けはバイナリ配布がメインで、ソースは開発者向けです、 という場合であれば、あまり問題にならないのかもしれませんが、 非開発者向けもソース配布、ですと大きな問題です。 かなり寛容なライセンスなので、自分のソースに同梱してしまおうか、 とも考えたのですが、そうするとかなりサイズが大きくなってしまいます。

flags.hh

getopt () のラッパーで、C++ のインタフェースになっている flags.hh です。かなりよい、と思うのですが、内部で getopt_long () を使っているのが、なんとも。 C++ からは使いやすくなっていると思います。 ですが、getopt 系はグローバル変数を使っていたりするため、 複数同時に使ったりなどすると確実におかしくなってしまいます。 (複数同時なんて、やるわけないだろ、というはごもっともではあります。) ラップしたことによって、うまく見えないようにしていると思うのですが、 結局、隠れているだけで解決したわけではありません。

cmdline

C++ 向け簡易コマンドラインパーザ (GitHub) です。 ヘッダファイル一つだけ、インストール不要、というところが素晴らしいです。 が、少々機能が乏しいように感じます。

実装

というわけで、結局自分で都合のいいように実装してしまいました。 基本的な解析の機能は getopt_long () の動作を参考に、 ほぼ準拠するような形で作ったつもりです。 ですので、一部省略した長いオプション (例えば、本当のオプションは --help だけど、短くした --he 等でも認識する、といった機能)なども動作します。

ヘッダファイル一つだけ、インストール不要、という点については、 cmdline や flags.hh のコンセプトを踏襲したものです。 C++11 向け、としたのは、std::function や クロージャを使えばシンプルに実装できるのでは、と思い付いたからです。 でも、よく見てみると flags.hh は std::function と クロージャと、使っていますね。 また、私がかかわっている LilyPond では、公式バイナリビルドシステム GUB は GCC 4.9 になっていますので、C++11 の利用は問題なかった、 というのも大きいです。

詳しい使い方などはドキュメントをご覧ください。 日本語 / 英語 両方用意してみました。