gintenlib::options ライブラリ

銀天ライブラリは ver1.0.0 から options ライブラリを公開します。
このライブラリは <unistd.h> に収録されている getopt 関数の代替として使うことの出来る、プログラムオプション解析用のライブラリです。
boost にも同名の options ライブラリが存在しますが、銀天ライブラリの options は、それよりもずっと単純なライブラリです。実質的に出来ることは getopt 関数と全く変わりません。
ちょっとしたオプション解析をしたいけれども何となく getopt は使いたくない、けど boost::options ではオーバースペックだ、などという状況で使うことが出来ます。また、ライブラリのリンクが要らない点もメリットとなっています。



それでは実際に、使っているところを見てみましょう:

#include <gintenlib/options.hpp>

#include <iostream>
using namespace std;

int main( int argc, char* argv[] )
{
  try
  {
    // a は引数なし、 b は引数あり、 c は引数はあってもなくてもいい
    gintenlib::options opt( argc, argv, "ab:c::" );
    
    for(;;)
    {
      // 解析させる。 ch にはオプションの文字が入る
      int ch = opt();
      if( ch == -1 ){ break; }  // -1: 解析終わり
      
      switch(ch)
      {
       case 'a':
        cout << "option 'a' is given.\n";
        break;
        
       case 'b':
        // 引数は optarg() で獲得
        cout << "option 'b' is given.\n"
             << "  argument: " << opt.optarg() << endl;
        break;
        
       case 'c':
        // 引数は optarg() で獲得
        cout << "option 'c' is given.\n";
        if( !opt.optarg().empty() ) // 引数が無い場合は string() が入る
        {
          cout << "  argument: " << opt.optarg() << endl;
        }
        break;
        
       default:
        // 未知のオプションがあった場合は例外が投げられるはずなのでここには来ない
        assert( !"should not get here." );
      }
    }
    
    // 解析終了。余ったオプションは argv[opt.optind()] 〜 argv[argc-1] に集められる
    // デフォルトでは、オプション引数が非オプション引数の後にある場合、 argv の順序を入れ替える
    // この動作を避けたい場合は、コンストラクタの引数を '+' で始めればよい
    cout << "extra options are:\n";
    for( int i = opt.optind(); i < argc; ++i )
    {
      cout << argv[i] << endl;
    }
  }
  catch( gintenlib::option_error& e )
  {
    // オプション解析に失敗して例外が投げられた場合はこちらに飛ぶ
    cerr << e.what() << endl;
    cerr << "available options are: -a, -b arg, -c [arg].\n";
    
    return 1;
  }
  catch( std::exception& e )
  {
    // そのほかの例外
    cerr << e.what() << endl;
    return 1;
  }
}

実行例:

> ./a.out hoge -ab fuga piyo
option 'a' is given.
option 'b' is given.
  argument: fuga
extra options are:
hoge
piyo
>./a.out -c hoge
option 'c' is given.
extra options are:
hoge
>./a.out -choge
option 'c' is given.
  argument: hoge
extra options are:
>./a.out -xyz
unknown option -- x
available options are: -a, -b arg, -c [arg].

これは、オプション -a, -b, -c, それと幾つかの自由な引数(実際のプログラムでは恐らくファイル名でしょう)を受け取り、それを表示するだけのプログラムです。
getopt を使ったことのある人なら、おそらく問題なく理解できたのではないでしょうか。
実際 getopt 関数とは、コンストラクタでオプション指定文字列を渡すということ以外、殆ど変わらない使い勝手になっています。
インターフェイス以外での getopt 関数との差異は、

  1. グローバル変数を使わない
  2. オプション解析に失敗した場合、デフォルトで例外を投げる
  3. 環境変数 POSIXLY_CORRECT には無関係に動作する
  4. '-' で始まるオプション文字列に非対応(修正予定)

といったところでしょうか。なお例外に関しては、コンストラクタで渡す文字列の先頭に ':' をつけることによって、例外を投げないようにすることができます。その場合、不明なオプション文字を渡された場合は '?' を返し、 optopt() メンバ関数を呼び出すことにより渡されたオプション文字を調べることが出来ます。
また getopt では、 optind 変数を変更することで、解析させたくない引数をスキップしたり、最初から引数解析をやり直せたり出来ますが、 options でも set_optind() 関数を用いて同様のことを行えます。
これらを総合して、 gintenlib::options は「ちょっとだけ洗練された getopt の代替」として使うことが可能となっています。また銀天ライブラリにしては珍しくテンプレートを使ってないライブラリなので、テンプレートがどうも使いにくい、という人でも安心して使える便利設計です。気に入ったら使ってみてください(まだダウンロードできないですが)。


以下はライブラリの仕様です:

gintenlib::options 仕様

収録ヘッダ
<gintenlib/options/options.hpp>内。<gintenlib/options.hpp>でも使える。例外クラスは<gintenlib/options/exceptions.hpp>内で定義される(自動インクルード)
名前空間
gintenlib
ライブラリのリンク
必要なし
namespace gintenlib
{
  struct options
  {
    // 指定された文字列でオプション解析オブジェクトを作る
    // 文字列は基本的に getopt 互換、冒頭の : と - の動作のみ違う
    explicit options( const std::string& optstr, int argc, char* argv[] );
    
    // 実際にオプションを解析する
    typedef int result_type;
    result_type operator()();
    
    // 次に処理する引数のインデックス
    int optind() const;
    // 次に処理する引数のインデックスを設定する
    void set_optind( int new_index = 1 );
    
    // 引数
    const std::string& optarg() const;
    
    // エラー時に読んでいた文字
    int optopt() const;
    
  };  // struct options
  
  // 例外基本クラス
  struct option_error : std::runtime_error
  {
    explicit option_error( const std::string& what_ );
  };
  // オプション指定文字列がおかしい
  struct invalid_option_string : option_error
  {
    explicit invalid_option_string( const std::string& optstr );
  };
  // 指定されないオプションが渡された
  struct unknown_option : option_error
  {
    explicit unknown_option( const std::string& opt );
  };
  // 引数が必要なのに渡されなかった
  struct option_requires_argument : option_error
  {
    explicit option_requires_argument( const std::string& opt );
  };
  
} // namespace gintenlib
構築/破棄
explicit options( int argc, char* argv[], const std::string& optstr );
機能
オプション指定文字列 optstr および解析する引数の格納された argc, argv から options オブジェクトを構築する。 optstr は受け付けるオプション文字を並べた文字列である。文字のあとにコロン( ':' )が置かれている場合、そのオプションには引数が必要であることを示す。ふたつ連続してコロンが置かれている場合、そのオプションは引数をとってもいいし、とらなくてもよい。またオプション指定文字列の先頭に '+'':' を置くこともできる。その場合の動作は operator()() の項を参照。
例外
optstr がオプション指定文字列として不正な場合、gintenlib::invalid_option_string 例外を投げる。
解析処理
typedef int result_type;
result_type operator()();
機能
予め与えられた引数を解析し、オプション文字を得る。
戻値
解析に成功した場合は得られたオプション文字を、これ以上オプションが発見できなかった場合は -1 を返す。
オプション指定文字列が ':' で始まる場合、不明なオプション文字を得た場合は '?' を、引数の必要なオプションに引数が与えられていなかった場合には ':' を、それぞれ返す。
副作用
解析の結果、非オプション文字列より後ろにオプション文字列が来る場合、コンストラクタで与えられた argv の並び順を変更し、オプション文字列が常に非オプション文字列よりも前に出るようにする。
オプション指定文字列が '-' で始まっている場合、この作用は行われず、非オプション文字列を発見した時点で -1 を返す。
例外
オプション指定文字列で与えられなかったオプション文字を発見した場合は gintenlib::unknown_option 例外を、引数を取ると指定されたオプション文字に引数が添えられていない場合は gintenlib::option_requires_argument 例外を、それぞれ投げる。オプション指定文字列 optstr の先頭が ':' で始まっている場合には、これらの動作は行われない。
int optind() const;
機能
次に処理する引数のインデックスを得る
戻値
次に operator()() で処理する引数のインデックスを得る。 operator()() の戻り値として -1 を得た場合には、非オプション文字列の始まるインデックスとなる。また -abc といった複数のオプションを指定した引数の処理中は、その引数のインデックスを返す。
例外
なし
void set_optind( int new_index = 1 );
機能
次に処理する引数のインデックスを指定する。この関数を呼び出した時点で、 argv の順序を除く全ての状態がリセットされるため、ユーザは特に気を使うことなく再び解析を行ったり、不要な引数を飛ばしたりできる。
例外
なし
情報取得
const std::string& optarg() const;
機能
オプションの引数を得る
戻値
引数が必要である、あるいは引数をつけてもいい、と指定されたオプションを解析した後に呼び出されたとき、その引数を返す。引数が無い場合は空文字列を返す。
例外
なし
int optopt() const;
機能
エラー時のオプション文字を得る
戻値
解析に失敗した後にこの関数を呼ぶと、そのとき解析したオプション文字を返す。エラーが起きていない時は文字 '?' を返す。
例外
なし
例外クラス

options で投げられる例外たち。これら全ての例外クラスは std::runtime_error を基底クラスとして持つため、 virtual const char* what() const throw (); 関数を暗黙のメンバとして保持する。

struct option_error : std::runtime_error
{
  explicit option_error( const std::string& what_ );
};
説明
options ライブラリで投げられる例外の基底クラス。
コンストラクタの引数
what() 関数で返される文字列。
struct invalid_option_string : option_error
{
  explicit invalid_option_string( const std::string& optstr );
};
説明
optionsのコンストラクタで与えられたオプション指定文字列が不正な場合に投げられる例外。
コンストラクタの引数
optionsのコンストラクタで与えられた不正な optstr を渡す。
struct unknown_option : option_error
{
  explicit unknown_option( const std::string& opt );
};
説明
オプションを解析した結果、オプション指定文字列になかったオプション文字を得た場合に投げられる例外。
コンストラクタの引数
そのオプション文字を渡す。
struct option_requires_argument : option_error
{
  explicit option_requires_argument( const std::string& opt );
};
説明
オプションを解析した結果、引数が必要なオプションに引数が渡されていない場合に投げられる例外。
コンストラクタの引数
そのオプション文字を渡す。
備考

引数をとるオプション文字に対する動作は、以下のように規定できる。まず "-oarg" のように、同一の argv 要素にオプション文字(この例では 'o' )に続けて文字列が存在する場合、 optarg() を呼び出した時、そのオプション文字(この例では 'o' )に続く文字列(この例では "arg" )を引数として得ることが出来る。そうで無い場合、つまり "-o arg" (これらは実際には別の argv 要素に格納される)と指定された場合は、そのオプション文字(この例では 'o' )の次の argv 要素(この例では "arg" )を optarg() により取得できる。
また、ふたつの ':' を伴った、引数を「取りうる」オプション文字に対する動作は、上の例で言う "-oarg" のように、同一の argv 要素内にオプションが指定されているときに限り、引数をとることができる。そうでない場合は optarg() の結果は空文字列となる。
以下は用語の説明である。
「オプション指定文字列」とは、options のコンストラクタの第一引数に渡される文字列のことである。受け入れるオプション文字を並べて記述する。
「オプション文字」とは、「オプション指定文字列」の構成要素として指令される各々の文字のことである。
「オプション文字列」とは、 argv の構成要素のうち、 "-abcd" のような '-' で始まる文字列のことである。ただし "--" は「オプション解析をここで打ち切る」という意味であり、オプション文字列としては扱わない。
「非オプション文字列」とは、 argv の構成要素のうち、 "hoge.txt" のような '-' で始まらない文字列のことである。ただし '-' で始まる文字列であっても、 "--" の後に現れる場合には、非オプション文字列として扱われる。このとき "--" 自身は非オプション文字列には入らない。また、"-o arg""arg" のような、引数を取るオプションの次の文字列もまた、非オプション文字列とは言われない。

todo

getopt_long に対応する options_long の開発