浮動小数点数配列パース用ファンクタ

今日のネタは浮動小数点数の1次元配列を便利に読み込みできるファンクタだよ。浮動小数点数の一次元配列があって、2つずつ読んで数値ペアとして解釈したりとか、3つずつ読んで、3次元ベクトルの3つの軸成分として解釈したりとか、みたいなそういう浮動小数点の一次元配列を読み込まなくてはならない場合って結構あるよね。とくにバイナリフォーマットの解読なんかではそういうことが多い。でももういちいちそういうわかりきったfor分書くのってかなりかったるかったりします。そんなときに便利なのが今日紹介する、ParseFloatVecだ。

template< class Func >
struct ParseFloatVec_t
{
  template< class T > struct remove_constref
  {  typedef T out_type; };
  template< class T > struct remove_constref< const T >
  {  typedef T out_type; };
  template< class T > struct remove_constref< T& >
  {  typedef T out_type; };
  template< class T > struct remove_constref< const T& >
  {  typedef T out_type; };
  typedef typename remove_constref< typename Func::argument_type >::out_type Vtype;
  ParseFloatVec_t( Func func, const int n ): _func(func), _n(n){ _cnt=-1;}
  void operator()( const typename Vtype::float_type& t ) const {
    _tmpv[++_cnt] = t;
    if( _cnt==_n-1 ){
      _func( _tmpv ); _cnt=-1;
    }
  }
  mutable int _n;
  mutable int _cnt;
  Func _func;
  mutable Vtype _tmpv;
};
template< class Func >
ParseFloatVec_t< Func > ParseFloatVec( Func func, const int n )
{
  return ParseFloatVec_t< Func >( func, n );
}

これの使い方は例えば以下のような数個の浮動小数点からなる構造体があったとして、

struct AB
{
  AB( void ){}
  float& operator[]( const int i ){ return (&_a)[i]; }
  void Hi( void ) const { cout<< _a<<", "<<_b<<endl; }
  float _a, _b;
  typedef float float_type;
};

struct XYZ
{
  XYZ( void ){}
  double& operator[]( const int i ){ return (&_x)[i]; }
  void Hi( void ) const { cout<< _x<<", "<<_y<<", "<<_z<<endl; }
  double _x, _y, _z;
  typedef double float_type;
};

これを引数にとるような以下の関数があったとする。

void HiAB( const AB& t ){ t.Hi(); return; }
void HiXYZ( XYZ& t ){ t.Hi(); return; }

ParseFloatVecを使えば、

  // float 列を、連続する2つを単位としてABに読み込み
  for_each( fl.begin(), fl.end(), ParseFloatVec( ptr_fun( HiAB ), 2 ) );
  // float 列を、連続する3つを単位としてXYZに読み込み
  for_each( fl.begin(), fl.end(), ParseFloatVec( ptr_fun( HiXYZ ), 3 ) );

このように配列の値を一つずつ読み込み、指定した回数だけ浮動小数点を読み込んだ時点で、それを、指定した関数にそのまま投げてくれるというものだ。しかも再構成すべき型は、指定した関数の入力引数から自動で認識してくれる。
指定すべきは「いくつ読み込んだら」ターゲットの型を再構成して指定の処理を呼び出すのかという情報だけだよ。ああ、ただし、注意が必要なのは、ターゲットの型では float_type が、何がしかの型としてtypedef されている必要があるから気をつけてくれよな。
以下は出力例だよ。

0.00125126, 0.563585
0.193304, 0.80874
0.585009, 0.479873
0.350291, 0.895962
0.82284, 0.746605
0.174108, 0.858943

0.00125126, 0.563585, 0.193304
0.808741, 0.585009, 0.479873
0.350291, 0.895962, 0.82284
0.746605, 0.174108, 0.858943

同じ1次元の浮動小数点値が、ちゃんと2つずつ読まれてペアとして認識されたり、3つずつ認識されたりしている様子がわかると思う。