おしゃれなCLI Argument Parser、Docopt.cppを使ってみた

GitHub - docopt/docopt.cpp: C++11 port of docopt

Pythonとかの情報はあるのだがC++はあんまりないので書いとく。

Docoptはgetoptやprogram_optionsのようなCLIから実行するときのコマンドやオプションを解析してくれるパーサなのだが、とにかく使い方がエレガントだ。注意点はGCC4.8だとなんかいろいろ大変ってことだろうか。GCC4.8を使っている場合については後述。

基本

かきかたはgithubにも書いてあるし、ここにもあるが、違うサンプルでかいてみる。

#include <docopt.h>  //別にコードと同じ階層にdocoptのライブラリをコピーしてきても良いが綺麗じゃないのでわけた
#include <iostream>

//USAGEで定義する文字列の中にUsage: Options: でUsageとOptionsを指定するだけであとはよろしくやってくれる
//exampleではなぜかcharを使っているが実装はstd::stringだった。いやまぁ暗黙で変換してくれるけどさ…
static const std::string USAGE =
R"(test
    Usage:
        test start (o <option> -p <option2> | -q <option3> [-d dir -v]
        test stop [now]
        test (-h | --help)
        test --version

    Options:
        -o option         Set option
        -p option2        Set option2
        -q option3        Set option3
        -d dir            Set directory
        -v                Debug print
        -h --help         Print help
        --version         Print version
)";

int main(int argc, const char** argv)
{
    //コマンドラインで指定する引数を最初のバイナリ名だけは飛ばして最後までvectorに格納するらしい
    std::vector<std::string> arg {argv + 1, argv + argc};
    //最後の引数に表示するバージョンスクリプトを指定
    std::map<std::string, docopt::value> args = docopt::docopt(USAGE, arg, true, "SampleProgram v0.0.1");

    //mapのキーはOptionsで指定したオプション名。値を取らないオプションの場合、docopt::valueはboolになる。それ以外はstd::string
    //helpとversionは特に実装しなくていいが、それ以外のオプションについては以降で処理を記述する
    //例
    if(args["start"].asBool() == true)
    {
        std::cout << "stat" << std::endl;
        if((std::string)args["-o"].asString() != "")
        {
            //do something
        }
    }
    
    return 0;
}

Usageを書けばそれにそってパースしてくれるというのがスマートでよい。コンパイルする場合はlibdocopt.aをリンクしよう。

GCC4.9以上が普通に入ってる場合

$ g++ -o test test.cpp -I<docoptdir> -ldocopt

GCC4.8以下の場合でGCC4.9を無理やり入れた場合は

$ g++-4.9 -o test test.cpp -I<docoptdir> -L/usr/lib/gcc/x86_64-linux-gnu/4.9 -lstdc++ -ldocopt 

libstdc++のリンクをしないといけない。

GCC4.9を無理やり入れてGCC4.9でdocoptをコンパイルする方法

C++11 regex using g++ | GeekWentFreak

ここにかいてあるとおりやればよい。

$ sudo add-apt-repository ppa:ubuntu-toolchain-r/test
$ sudo apt-get update
$ sudo apt-get install g++-4.9

無理にgcc4.9を使うのでCMakeList.txtをいじってもいいけど自分でコンパイルすることにする(cmakeはよくわからん)

$ g++-4.9 --std=c++11 -D__cplusplus=201103L -D__GXX_EXPERIMENTAL_CXX0X__ -o docopt.o docopt.cpp docopt.h docopt_value.h docopt_util.h docopt_private.h 
$ ar -r "libdocopt.a" ./docopt.o

あれ?これでいいのかな?まぁEclipseで適当に設定してShared Libraryを作ってください
これでできたlibdocopt.aを使わないとregex関係のリンクが上手くできない


ちなみにgcc4.9以上が入っててもCMake3.2未満だとコンパイルできないのでとりあえずcmake3.2は入れる。

$ sudo apt-get install build-essential
$ wget http://www.cmake.org/files/v3.2/cmake-3.2.2.tar.gz
$ tar xf cmake-3.2.2.tar.gz
$ cd cmake-3.2.2
$ ./configure
$ make
$ sudo apt-get install checkinstall
$ sudo checkinstall
$ sudo make install

そんでおもむろにライブラリを作る。

$ cmake .
$ make

一度つかえるようになればかなり相当楽になる。