NestCall の1引数版、NestCall1

今日は無限階層呼び出しNestCallの1引数受け取りバージョンである、NestCall1の紹介だよ。まずは以下がnurs::nest_call1のソースだ。

// nest_call1
template< class Fnc1, class Fnc2 >
struct nest_call1_t: public binary_function
  < typename Fnc1::argument_type, 
    typename Fnc2::second_argument_type, typename Fnc2::result_type >
{
  nest_call1_t( Fnc1 fc1, Fnc2 fc2 )
    : _fc1(fc1), _fc2(fc2)
  {}
  typename Fnc2::result_type 
    operator()( 
    typename Fnc1::argument_type arg1, 
    typename Fnc2::second_argument_type arg2 ) const 
  {
    return _fc2( _fc1( arg1 ), arg2 );
  }
  mutable Fnc1 _fc1;
  mutable Fnc2 _fc2;
};
template< class Fnc1, class Fnc2 >
nest_call1_t< Fnc1, Fnc2 > 
nest_call1(  Fnc1 fc1, Fnc2 fc2 ){ 
  return nest_call1_t< Fnc1, Fnc2 >( fc1, fc2 );
}

使い方はこうだ。例えば以下のように、Rolling,Hot,Vermillionの3つのクラスからなる構造があり、

struct Rolling;

struct Vermillion
{
  Vermillion( void ){ _val=-1; _r=0; }
  void Hi( void ){ cout<< _val<<", "<< _r << endl; }
  void setVal( const int i ){ _val=i; }
  void setRolling( Rolling* r ){ _r=r; }
  int _val;
  Rolling* _r;
};

struct Hot
{
  Hot( void ){ _vermillion = new Vermillion; }
  ~Hot( void ){ delete _vermillion; }
  Vermillion* getVermillion( void ){ return _vermillion; }
  Vermillion* _vermillion;
};

struct Rolling
{
  Rolling( void ){ _hot = new Hot; _val=rand(); }
  ~Rolling( void ){ delete _hot; }
  Hot* getHot( void ){ return _hot; }
  int getVal( void ){ return _val; }
  Hot* _hot;
  int _val;
  static Rolling* Create( void ){ return new Rolling; }
};

以下のようにRollingが1ダース作成されていたとしよう。

  vector< Rolling* > vr( 12 );
  {
    generate( vr.begin(), vr.end(), Rolling::Create );
  }

このとき、本来なら

  for each( Rolling* iroll in vr ){
    iroll->getHot()->getVermillion()->setVal( 0 );
  }// iroll

と書かなくてはならなかったところを、NestCall1を使うと、以下のように書けてしまう!

  for_each( vr.begin(), vr.end(), 
    bind2nd(
    nest_call1( mem_fun( &Rolling::getHot ), 
    nest_call1( mem_fun( &Hot::getVermillion ),
    mem_fun1( &Vermillion::setVal ) ) ) 
    , 0 )
    );

どうだろう。随分コードが見やすくなったと思わないだろうか?irollというどうでもいい変数名を考える必要すらなくなったところとか。え、共感しかねる?どうしてそんなこと言うのかい?
ソースを見ればわかると思うけど前回のNestCallがunary_function だったのに対して、今回のnest_call1は、binary_functionだ。そしてその真髄は、

 a->getB->set( val );

と書いておったものを、

 nest_call1( 
    mem_fun(&A::getB ), mem_fun1(&B::set) 
  )( a, val )

と、書けるというところにある。
いかがだろうか。分かりやすく言えば、

 a/**/->getB()->getC()->getD()->.....->set( val/***/ );

と書かれているものを、

  Func( a/**/, val/***/ ); 

として呼び出せるような、binary_function「Func」を作ってくれる。そんな関数アダプタだよ。
つまり、nest_call1の第一引数にunary_functionを、そして第二引数にbinary_functionをとって、それらを上記のように合成して、全く新しいbinary_function (上記のFunc)を返すという代物だ。
なので、nest_call1をネストして使いたい場合は、第二引数のところで、nest_call1自身の数珠繋ぎをすることになるというわけだ。nest_callとの混在は許されないってことなんだよ。だって最後にbind2ndにはbinary_functionを渡さなくてはならないからね。
気をつけてほしいのは、上記の操作は実はただのunary_functionである、nest_callのみを使って、

  for_each( vr.begin(), vr.end(), 
    nest_call( mem_fun( &Rolling::getHot ), 
    nest_call( mem_fun( &Hot::getVermillion ),
    bind2nd( mem_fun1( &Vermillion::setVal ) , 0)
    ) ) );

このようにしても実現できる。しかし、nest_callとnest_call1との決定的で重要な違いは、最終的に関数合成した後にできるのが、前者はunary_functionであるのに対し、後者はbinary_functionであるということだ。この違いが今後紹介するnurs::bind_binaryアダプタを使えるか使えないかということの重要な決め手となってくるのである。