delegate2
delegate1-boost::signalを読む - while( c++ );の続き。
何となくboost::signalの仕組みがわかってきたので実装してみます。
目標
int hoge( int, float ) { return 1; } struct piyo { int operator()( int, float ) { return 2; } }; class fuga { public: int func( int, float ) { return 3; } }; //すべての関数の戻り値を返すcombiner template< typename Container > struct aggregate_values { typedef Container result_type; template< typename InputIterator > result_type operator()( InputIterator first, InputIterator last ) { return result_type( first, last ); } }; int main() { { delegate< int ( int, float ) > a; a += hoge; a += piyo(); a += bind( &fuga::func, new fuga, _1, _2 ); int b = a( 1, 2.0f ); //デフォルトで最後に登録した関数の戻り値が返ってくる } { typedef delegate < int ( int, float ) , aggregate_values< std::vector< int > > > test_delegate; test_delegate a; a += hoge; a += piyo(); a += bind( &fuga::func, new fuga, _1, _2 ); test_delegate::result_type b = a( 1, 2.0f ); //b = { 1, 2, 3 } } return 0; }
delegate
- 複数の関数の登録と呼び出し
- 各関数の戻り値をcombinerで統合し、単一の値として返す
//基本形 template < typename Signature // R ( A1, A2, ..., AN ) > class delegate; //引数無し〜N個までのdelegate template < typename R , typename A1, typename A2, ..., typename AN > class delegate< R ( A1, A2, ..., AN ) > { typedef function< R ( R1, R2, ..., AN ) > func_type; typedef std::list< func_type > func_list; typedef ... Combiner; typedef typename Combiner::result_type result_type; private: func_list func; public: template< typename F > void operator+=( F f ) { func.push_back( f ); } result_type operator()( A1 a1, A2 a2, ..., AN aN ) { //戻り値は統合子で返す Combiner combiner; return combiner( call_iterator( func.begin() ), call_iterator( func.end() ) ); } };
last_value
- デフォルトのcombiner
- 最後に呼び出した関数の戻り値を返す
- InputIterator::operator*()で関数の呼び出し
template< typename R > struct last_value { typedef R result_type; template< typename InputIterator > result_type operator()( InputIterator first, InputIterator last ) { result_type ret = result_type(); while( first != last ) ret = *( first ++ ); return ret; } }; template<> struct last_value< void > { typedef unusable result_type; template< typename InputIterator > result_type operator()( InputIterator first, InputIterator last ) { while( first != last ) *( first ++ ); return result_type(); } };
delegateの修正
- last_valueをデフォルトに
//基本形 template < typename Signature // R ( A1, A2, ..., AN ) , typename Combiner = last_value< typename function_traits< Signature >::result_type > , typename Function = function< Signature > > class delegate; //引数無し〜N個までのdelegate template < typename R , typename A1, typename A2, ..., typename AN , typename Combiner , typename Function > class delegate< R ( A1, A2, ..., AN ), Combiner, Function > { typedef R ( *func_type )( R1, R2, ..., AN ) typedef std::list< func_type > func_list; typedef Combiner combiner_type; typedef typename combiner_type::result_type result_type; /*....*/
iterator_core_access、iterator_facade、call_iterator
combinerで各関数を呼び出すためのiteratorを作ります。ちゃんと標準に合った形のiteratorを自作するのは大変で、本来はboost::iterator_facadeを利用すべきですが、なんか負けた気がするのでわかる範囲で実装してみます。たぶん他の用途で使えませんw
iterator_core_access
class iterator_core_access { public: template< typename Facade > static typename Facade::reference dereference( Facade const& f ) { return f.dereference(); } template< typename Facade > static void increment( Facade& f ) { f.increment(); } template< typename Facade > static void decrement( Facade& f ) { f.decrement(); } template< typename Facade > static bool equal( Facade const& f1, Facade const& f2 ) { return f1.equal( f2 ); } template< typename Facade > static void advance( Facade& f, typename Facade::difference_type n ) { f.advance( n ); } private: iterator_core_access(); };
iterator_facade
- operator*、operator++、operator!=などのiteratorの操作に最低限必要な演算子を定義
- CRTP(Curiously Recursive Template Pattern)で実装
- iterator_core_accessを介して派生クラスの関数が呼ばれる。
- 標準を使うのは負けじゃないですよね?
template < typename Derived , typename Value , typename Category > class iterator_facade : public std::iterator< Category, Value > { public: Derived& derived() { return *( static_cast< Derived* >( this ) ); } Derived const& derived()const { return *( static_cast< Derived* >( this ) ); } public: reference operator*() { return iterator_core_access::dereference( derived() ); } Derived& operator++() { iterator_core_access::increment( derived() ); return derived(); } Derived operator++( int ) { Derived temp( derived() ); ++( *this ); return temp; } Derived& operator--() { iterator_core_access::decrement( derived() ); return derived(); } Derived& operator+=( difference_type n ) { iterator_core_access::advance( derived(), n ); return derived(); } Derived& operator-=( difference_type n ) { iterator_core_access::advance( derived(), -n ); return derived(); } bool operator==( Derived const& r ) { return iterator_core_access::equal( derived(), r ); } bool operator!=( Derived const& r ) { return !iterator_core_access::equal( derived(), r ); } };
call_iterator
- とりあえずInputIterator(SinglePass Readable Iterator)として実装
- dereference
- increment
- equal
- すべてprivate(iterator_core_accessのみに公開)
template< typename Invoker, typename Iterator > class call_iterator : public iterator_facade < call_iterator< Invoker, Iterator > , typename Function::result_type , std::input_iterator_tag > { typedef typename result_t< typename Invoker::result_type >::type result_type; private: Invoker invoker; //delegateの引数を格納したInvokerクラス Iterator iter; //functionを指す result_type* result_value; public: call_iterator( Iterator iter, Invoker invoker, result_type* result_value ) : invoker( invoker ), iter( iter ), result_value( result_value ) {} private: friend class iterator_core_access; //登録済みの関数の呼び出しと戻り値の取得 reference dereference()const { *result_value = invoker( *iter ); //ちょっと気持ち悪い。。。 return *result_value; } void increment() { ++ iter; } bool equal( call_iterator const& r )const { return iter == r.iter; } };
Invoker
boost::signal::detail::call_boundN< R >::caller< A1, ..., AN >の部分。
call_boundの意味が良くわからなかったので、invokerとしました。
- 引数の保持
- 関数の呼び出し
template < typename R , typename A1, typename A2, ..., AN , typename Combiner , typename Function > class delegate< R ( A1, A2, ..., AN ), Combiner, Function > { //引数の保持と実際の呼び出し template< typename R, typename A1, typename A2, ..., typename AN > struct invoker { typedef typename result_t< R >::type result_type; A1 a1; A2 a2; ... AN aN; invoker( A1 a1, A2 a2, ..., AN aN ) : a1( a1 ), a2( a2 ), ..., aN( aN ) {} template< typename F > result_type operator()( F f )const { return f( a1, a2, ..., aN ); } }; //戻り値voidの特殊化 template< typename A1, typename A2, ..., typename AN > struct invoker< void, A1, A2, ..., AN > { //void -> unusable typedef typename result_t< void >::type result_type; result_type result_value; A1 a1; A2 a2; ... AN aN; invoker( A1 a1, A2 a2, ..., AN aN ) : result_value() , a1( a1 ), a2( a2 ), ..., aN( aN ) {} template< typename F > result_type operator()( F f )const { f( a1, a2, ..., aN ); return result_value; } }; /*...*/ };
delegate::operator()の修正
- call_iteratorにinvokerと戻り値格納用の変数のアドレスを渡す
result_type operator()( A1 a1, A2 a2, ..., AN aN ) { typedef typename invoker_type::result_type result_type; invoker_type invoker( a1, a2, ..., aN ); result_type result_value = result_type(); combiner_type combiner; return combiner( call_iterator( func.begin(), invoker, &result_value ), call_iterator( func.end(), invoker, &result_value ) ); }
後はperlで適当に引数無し〜N個まで用意すれば完成です。
現時点で関数の削除は出来ません。
operator-=で削除する場合、関数同士の比較が必要ですよね。
ということは、関数オブジェクトはどのように比較すれば良いのだろうと。
その他足りない物も次回へ持ち越しです。
うーん。。。いろいろと酷いですね。。。
特にイテレータ関連の理解不足が大きいです。
標準のイテレータも実装しつつ、データ構造、アルゴリズム、文字列、ストリームなども自作すべきでしょうか。。。
download
- delegate20081226.zip
- VC++2008ExpressEdition用