本日の関数アダプタnurs::bind_binary

今日はnurs::bind_binary の紹介だよ。bind_binaryは、予め渡されたbinary_functionを、unary_functionとして呼び出せるようにするための関数アダプタだよ。前回のNestCall1と併用することによってこれまた高階関数で記述できる処理の幅がぐっと増すことになるよ。
早速見て行こうか。まずはbind_binaryのソースを以下に示そう。

  // bind_binary: 
  template < class Func >
  struct bind_binary_t: public std::unary_function
    < typename Func::first_argument_type, typename Func::result_type >
  {
    bind_binary_t( const Func& func ): _func( func ){}
    typename Func::result_type operator()
      ( typename Func::first_argument_type arg ) const {
        _func( arg, arg );
    }
    Func _func;
  };
  template< class Func >
  bind_binary_t< Func > bind_binary( Func func ){ 
    return bind_binary_t< Func >( func ); 
  }
  template < class Func, class Prop >
  struct bind_binary2_t: public std::unary_function
    < typename Func::first_argument_type, typename Func::result_type >
  {
    bind_binary2_t( const Func& func, const Prop& prop ): _func( func ), _prop( prop ){}
    typename Func::result_type operator()
      ( typename Func::first_argument_type arg ) const {
        _func( arg, _prop( arg ) );
    }
    Func _func;
    Prop  _prop;
  };
  template< class Func, class Prop >
  bind_binary2_t< Func, Prop > bind_binary( Func func, Prop prop ){ 
    return bind_binary2_t< Func, Prop >( func, prop ); 
  }

次に使い方の説明をするよ。
前回とまったく同じく、Rolling、Hot、Vermillionの3つのクラス構成があって、vrの中にRollingが1ダース作成されている状況を想定して欲しい。
この時、

  for each( Rolling* ir in vr ){
    ir->getHot()->getVermillion()->setRolling( ir );
  }// ir

と書いていたコードがあるとしよう。これはどうだろうか。最後のsetRollingの引数にir が来ているため、高階関数化するのはちょっと厄介だね。でもbind_binaryを使うと次のように書けてしまうのさ。

  for_each( vr.begin(), vr.end(), 
    bind_binary(
    nest_call1( mem_fun( &Rolling::getHot ), 
    nest_call1( mem_fun( &Hot::getVermillion ),
    mem_fun1( &Vermillion::setRolling ) ) ) 
    )
    );

どうかな、謎の変数名irを考えなくても、同じ処理ができてしまうということでとても見やすいコードになったね。何より一行で記述できるのが気分いいね。
bind_binaryは、それ自身unary_functionなんだけど、その初期化時にまずbinary_functionを受け取るところが特徴だ。bind_binaryがunary_functionとして呼ばれた時は、保持しているbinary_functionの両方の引数に、同じ値をつめて呼び出してくれるんだ。だから上記の例のように自分自身をメンバ関数の引数として呼び出す処理の表現に使えるというわけさ。上ではbind_binaryに渡すbinary_functionを、nest_call1によって作成しているというわけなんだね。だからここはnest_callではだめで、nest_call1でなくてはならないんだ。
こういう場合はどうだろう。

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

この場合はsetValの中で、ir->getVal()という具合に、irのプロパティを表現しているね。ちょっと手ごわいかな?
でも心配は無用だ。

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

そう。bind_binaryには、自身が保持しているbinary_functionを呼び出す際に、二つ目の引数に通過させたいフィルタ関数を指定することもできるんだ。上の場合は、mem_fun( &Rolling::getVal ) というのが、フィルタ関数として、bind_binary に渡されているよ。
どうだろう、ちょっとしたループだったらfor文に入れるどうでもいい反復変数名を何にしようかななんてのん気に考えてる暇があったら、さらっと一行で高階関数表記で記述してしまいたいものだよね。文明人として!