デリゲート
ちょっと仕事の都合上必要になったので急遽作成しました。かなりてこずるんじゃないかと思ったけど必死になれば1日で出来るもので・・・意外にも早くできました。しかも無駄にマルチキャスト付きで(^^;
デリゲートというのは直訳すると「委譲」であり、まあぶっちゃけた話、C言語で言うところの関数ポインタみたいなものである。詳しいことは私自身もよくわかっていないのでそのあたりは適当なページでも漁ってください(ぉ
とりあえず作ったデリゲートクラスの実装は以下の通り
template< class RETURN_VALUE, class ARGUMENT > class delegate1 { private: class DelegateBase{ public: virtual RETURN_VALUE Run(ARGUMENT) = 0; smart_ptr< DelegateBase > m_pNextDelegateBase; }; template< class CLASS_NAME > class ClassBase: public DelegateBase{ public: ClassBase< CLASS_NAME >( CLASS_NAME* pInstance, RETURN_VALUE (CLASS_NAME::*pMethod)(ARGUMENT) ): m_pInstance( pInstance ), m_pMethod( pMethod ){} virtual RETURN_VALUE Run( ARGUMENT arg ){ if( m_pNextDelegateBase != NULL ){ m_pNextDelegateBase->Run( arg ); } return (m_pInstance->*m_pMethod)( arg ); } private: CLASS_NAME* m_pInstance; RETURN_VALUE (CLASS_NAME::*m_pMethod)(ARGUMENT); }; template< class FUNCT > class Functor: public DelegateBase{ public: Functor< FUNCT >( FUNCT& Funct ):m_Functor( Funct ){} virtual RETURN_VALUE Run( ARGUMENT arg ){ if( m_pNextDelegateBase != NULL ){ m_pNextDelegateBase->Run( arg ); } return m_Functor( arg ); } private: FUNCT& m_Functor; }; public: delegate1(){} //メソッド用コンストラクタ template< class CLASS_NAME > delegate1( CLASS_NAME* pInstance, RETURN_VALUE (CLASS_NAME::*pMethod)(ARGUMENT) ){ m_pDelegateBase = new ClassBase< CLASS_NAME >( pInstance, pMethod ); } //生関数ポインタもしくは関数オブジェクト用コンストラクタ template< class FUNCT > delegate1( FUNCT& Funct ){ m_pDelegateBase = new Functor< FUNCT >( Funct ); } //呼び出し RETURN_VALUE operator()( ARGUMENT arg ){ return m_pDelegateBase->Run( arg ); } //マルチキャスト delegate1& operator+=( const delegate1& Dele ){ Dele.m_pDelegateBase->m_pNextDelegateBase = m_pDelegateBase; m_pDelegateBase = Dele.m_pDelegateBase; return *this; } //ぬるチェック bool IsNull(){ return m_pDelegateBase == NULL; } private: smart_ptr< DelegateBase > m_pDelegateBase; };
普通の関数はもちろん、メンバ関数や関数オブジェクトも受け取れるというなかなかの優れもの(ぇ 引数の数と型および戻り値の型さえ合っていればなんでも受け取ってくれる。
void Funct1( int i ); class ClassSample{ public: void Method( int i ); void operator()( int i ); }; delegate1< void, int > deleA( Funct1 ); deleA( 1 ); //←Funct(1)が呼ばれる ClassSample cs; delegate1< void, int > deleB( &cs, &(ClassSample::Method) ); deleB( 2 ); //←cs.Method(2)が呼ばれる delegate1< void, int > deleC( cs ); deleC( 3 ); //←cs(3)が呼ばれる delegate1< void, int > deleD; deleD += deleA += deleB += deleC; deleD( 4 ); //←Funct(4), cs.Method(4), cs(4)の順に呼ばれる
実装上での欠点として、引数の数が違うと別途クラスを用意しておかなければいけないこと。上記例の場合は引数が一つの場合である。実用上はどこまで用意しておく必要があるかは不明だが一応4つまでは用意してある。これも一つのクラスにまとめられたらすばらしいのだが文法上不可能なんじゃないかなとあきらめつつある今日この頃でした。