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_accessiterator_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