function7-placeholder
前回function6-bind - while( c++ );の続き。
preprocessorを使うのはある程度完成に近づいてからのほうが良さそうなので、先にplaceholderに対応させます。
今日の目標
- placeholderを使えるように。
class fuga { public: int func( int, float ) { std::cout << "fuga" << std::endl; return 2; } }; int main() { bind( &fuga::func, new fuga, 0, 1.0f )(); int x =0; bind( &fuga::func, _1, _2, 1.0f )( new fuga, x ); return 0; }
bind生成時ではなく、bindのoperator()呼び出し時に、引数を渡すことが可能です。これでstd::for_eachと一緒に使うことも出来ますね。
std::vector< fuga* > a; a.push_back( new fuga ); a.push_back( new fuga ); std::for_each( a.begin(), a.end(), xtl::bind( &fuga::func, _1, 0, 1.0f ) );
ということで、今回は_1、_2のようなplaceholderを渡せるようにbind、arg_listNを修正していきます。
arg
boost::bindで使用可能な_1、_2などのplaceholderは以下のようにanonymous-namespaceで定義されています。
namespace { /*...*/ static boost::arg<1> _1; static boost::arg<2> _2; static boost::arg<3> _3; static boost::arg<4> _4; static boost::arg<5> _5; static boost::arg<6> _6; static boost::arg<7> _7; static boost::arg<8> _8; static boost::arg<9> _9; /*...*/ } // unnamed namespace
テンプレート引数に整数値を渡して、arg< 1 >〜arg< 9 >まで別の型として定義されています。
このboost::argと同等のargを作ってみましょう。
template< int N > struct arg{};
以上です。簡単ですね。型を定義するだけなのでこれでOKです。
boostではplaceholderかどうか調べるためのメタ関数が用意されていますが、現状で必要ないので無視しておきます。
同様にplacceholder(_1〜_9)も作っておきます。boostと同じanonymous-namespaceに作るので、boostと同時に使うことは出来ません。
namespace{ static arg< 1 > _1; static arg< 2 > _2; static arg< 3 > _3; static arg< 4 > _4; static arg< 5 > _5; static arg< 6 > _6; static arg< 7 > _7; static arg< 8 > _8; static arg< 9 > _9; }
では、いきなり使ってみましょうか。
int hoge( int, float ) { std::cout << "hoge" << std::endl; return 1; } int main() { int x = 0; bind( hoge, _1, 1.0f )( x ); return 0; }
コンパイルエラーです。。。
前回function6-bind - while( c++ );の自作bindは以下のようになっていました。
//グローバル関数用 template< typename R //戻り値の型 , typename A1 //引数1の型 , typename A2 > bind_t< R, R ( * )( A1, A2 ), arg_list2< R, A1, A2 > > bind( R ( *f )( A1, A2 ), A1 a1, A2 a2 ) { typedef R result_type; typedef result_type ( *func_type )( A1, A2 ); typedef arg_list2< R, A1, A2 > arg_list; typedef bind_t< result_type, func_type, arg_list > bind_type; return bind_type( f, arg_list( a1, a2 ) ); }
bindの引数の関数ポインタ型がint (*)( arg<1>, float )になり、関数hogeを渡せなくなるようですね。関数の引数の型とbindの引数の型は異なるものにしたほうが良さそうです。
//グローバル関数用 template< typename R //関数の戻り値の型 , typename B1 //関数の引数1〜Nの型 , typename B2 , typename A1 //引数1〜Nの型 , typename A2 > bind_t< R, R ( * )( B1, B2 ), arg_list2< R, A1, A2 > > bind( R ( *f )( B1, B2 ), A1 a1, A2 a2 ) { typedef R result_type; typedef result_type ( *func_type )( B1, B2 ); typedef arg_list2< R, A1, A2 > arg_list; typedef bind_t< result_type, func_type, arg_list > bind_type; return bind_type( f, arg_list( a1, a2 ) ); }
コンパイルすると、、、
bindは大丈夫ですね。ただ、arg_list2でコンパイルエラーになります。
//引数2 /*...*/ class arg_list2 /*...*/ { /*...*/ public: template< typename F > result_type operator()( F& f ) { return f( a1, a2 );//←コンパイルエラー } };
今回fにはhogeが渡されているので、第1引数はintです。しかし、a1にはplaceholderの_1が格納されています。当然エラーですね。
- aNがplaceholderであれば、bind_t::operator()の対応する引数を、
- そうでなければ、aNをそのまま渡す
以上の点を修正する必要があります。
arg_list
- bind_t::operator()の引数を受け取る
- aNがplaceholder→受け取った引数
- aNがplacholderでない→aNをそのまま
オーバーロードで解決できそうです。
//引数2 /*...*/ class arg_list2 /*...*/ { /*...*/ public: template< typename FuncType, typename ArgList > result_type operator()( FuncType& f, ArgList& al ) { return f( al.get( a1 ), al.get( a2 ) ); } public: //引数がplaceholderであればメンバのaNを返す A1 get( arg< 1 >& )const{ return a1; } A2 get( arg< 2 >& )const{ return a2; } //placeholderでなければそのまま返す template< typename T > T get( T t )const{ return t; } };
同様に、arg_list0〜ard_list3を修正します。
//引数なし template< typename R > class arg_list0 { protected: typedef R result_type; public: template< typename FuncType, typename ArgList > result_type operator()( FuncType& func, ArgList& /*al*/ ) { return func(); } public: //引数がplaceholderでなければそのまま返す template< typename T > T get( T t )const{ return t; } }; //引数1 template< typename R , typename A1 > class arg_list1 : public arg_list0< R > { protected: A1 a1; public: arg_list1( A1 a1 ) : arg_list0() , a1( a1 ) {} public: template< typename FuncType, typename ArgList > result_type operator()( FuncType& func, ArgList& al ) { return func( al.get( a1 ) ); } public: //引数がplaceholderであればメンバのaNを返す A1 get( arg< 1 >& )const{ return a1; } //placeholderでなければそのまま返す template< typename T > T get( T t )const{ return t; } }; //引数2 template< typename R , typename A1 , typename A2 > class arg_list2 : public arg_list1< R, A1 > { protected: A2 a2; public: arg_list2( A1 a1, A2 a2 ) : arg_list1( a1 ) , a2( a2 ) {} public: template< typename FuncType, typename ArgList > result_type operator()( FuncType& func, ArgList& al ) { return func( al.get( a1 ), al.get( a2 ) ); } public: //引数がplaceholderであればメンバのaNを返す A1 get( arg< 1 >& )const{ return a1; } A2 get( arg< 2 >& )const{ return a2; } //placeholderでなければそのまま返す template< typename T > T get( T t )const{ return t; } }; //引数3 template< typename R , typename A1 , typename A2 , typename A3 > class arg_list3 : public arg_list2< R, A1, A2 > { protected: A3 a3; public: arg_list3( A1 a1, A2 a2, A3 a3 ) : arg_list2( a1, a2 ) , a3( a3 ) {} public: template< typename FuncType, typename ArgList > result_type operator()( FuncType& func, ArgList& al ) { return func( al.get( a1 ), al.get( a2 ), al.get( a3 ) ); } public: //引数がplaceholderであればメンバのaNを返す A1 get( arg< 1 >& )const{ return a1; } A2 get( arg< 2 >& )const{ return a2; } A3 get( arg< 3 >& )const{ return a3; } //placeholderでなければそのまま返す template< typename T > T get( T t )const{ return t; } };
bind_t
- 引数の個数分だけoperator()をオーバーロード
class bind_t { /*...*/ public: R operator()() { arg_list0< R > al; return l( f, al ); } template< typename A1 > R operator()( A1& a1 ) { arg_list1< R, A1 const& > al( a1 ); return l( f, al ); } template< typename A1, typename A2 > R operator()( A1& a1, A2& a2 ) { arg_list2< R, A1 const&, A2 const& > al( a1, a2 ); return l( f, al ); } template< typename A1, typename A2, typename A3 > R operator()( A1& a1, A2& a2, A3& a3 ) { arg_list2< R, A1 const&, A2 const&, A3 const& > al( a1, a2, a3 ); return l( f, al ); } };
実行結果
int hoge( int x, float y ) { std::cout << "hoge:" << x << "," << y << std::endl; return 1; } int main() { int x = 1; float y = 2.0f; bind( hoge, 0, 1.0f )(); //bind( hoge, 0, 1.0f )( 0 );//定数は不可 bind( hoge, 0, 1.0f )( x, y );//placeholderがなければ無視される bind( hoge, _1, 1.0f )( x ); bind( hoge, _1, _2 )( x, y ); bind( hoge, x, _1 )( y ); return 0; }
- bind_t::operator()で定数は渡せない(C++言語自体の制約?)
次回は
- preprocessorで2個以上引数に対応
- function+bind
の予定です。
正直preprocessorが面倒ですね。
全部マクロにしてしまうとデバッグしづらいですし。
boostでは一部perlを使ってソースを作っているようなので、私もperlを使ってみようと思っています。ちなみにperlの経験は、数年前に1日だけCGIで遊ぶときにかじった程度です。
これでようやくfunctionの続きに入れそうです。
download
- bind20081216.zip
- VC++2008ExpresEdition用
- サンプルではxtlというnamespaceを使っています。eXtensive Template Library的な何か。