ライブラリやアルゴリズムの内部に、外部モジュールで定義したコールバックを登録しておいて、アルゴリズムの方で何かあったら、その登録しておいたコールバックが呼ばれるようなしくみを作りたい。
例えばアルゴリズムが発したエラー通知やメッセージを受け取って、外部モジュールでそれを画面に表示したり、ファイルに記述したり、、あるいはアルゴリズムの要請を受けて、外部モジュールの方で新規にオブジェクトを生成したり、変更したり、削除したりする必要がある場合。つまりアルゴリズムからの通知を外部モジュールが受け取れるようなしくみを実現したい場合。
関数オブジェクトの説明ではよくある話だが、今回は関数オブジェクト、関数ポインタ、どちらでも受け取って、いつでもそれらをコールバックとして呼べるように保持しといてくれる、コールバックのカプセル化クラスを作成してみたのでメモ。
単に関数オブジェクトをアルゴリズムに登録しとけばいいんでないのという話だが、要は関数オブジェクトだろうが関数だろうがどちらでも受け取れるようなものを作りたかっただけ。
#include <iostream> using namespace std; // 汎用コールバック // // 関数オブジェクト、関数ポインタ、どちらでも受け取り、 // いつでもそれらをコールバックとして呼べるように保持しといてくれるクラス // // ポリシーインタフェースを満足する任意の // 関数オブジェクトあるいは関数ポインタ、 // とにかく何でも格納できるようにするために、 // 空のスタブクラスを基底に用いているのがミソ。 class CallBackStub{ public: virtual void operator()( const int i )=0; }; template< class Func > class CallBack: public CallBackStub { public: CallBack( Func& func ):m_f( func ){} void operator()( const int i ){ m_f( i ); };// ←Funcが満たすべきポリシー private: Func m_f;// ←(ポリシーを格納保持)(「呼べるもの」) }; // コールバック作成用ヘルパークラス // (Pred には、ポリシーを満たす関数オブジェクトあるいは関数を突っ込むと、 // それを保持した汎用コールバックのインスタンスが返る。 template< class Pred > CallBackStub* CreateCallBack( Pred pred ){ return new CallBack< Pred >( pred ); } // 上記任意型コールバック保持機構を利用した、 // アルゴリズムの実装例 // // class Algorithm { // 本クラスは、1から10まで数えるだけですが、コンストラクタで // 受け取る、コールバックの機能によってさまざまな振る舞いをします。 public: Algorithm( CallBackStub* cb): m_cb_stub( cb ){} ~Algorithm( void ){} void Exec( void ){ for( int i=0; i<10; ++i ){ m_cb_stub->operator ()( i ); }// i } private: CallBackStub* m_cb_stub; }; ///////////////////////// // 以下が、アルゴリズム利用コードです ////////////////////////// class FibonacciFunctor// 関数オブジェクトを用意 { public: FibonacciFunctor( void ){ m_p1=0; m_p2=1; } public: void operator()( const int i ){ cout << "im Functor--> "<< m_p2 << endl; int tmp = m_p1; m_p1 = m_p2; m_p2 = tmp+m_p2; }; private: int m_p1, m_p2; }; void PowFunction( const int i )// 関数を用意 { cout << "im Function()*** "<< i*i << endl; } void main( void ) { // アルゴリズムにコールバックをセット Algorithm al( CreateCallBack( FibonacciFunctor() ) ); Algorithm al2( CreateCallBack( PowFunction ) ); // 実行 al.Exec(); al2.Exec(); // このように、コールバックをセットするタイミングと、 // アルゴリズムを実行させるタイミングとを分けることができる。 // そんだけ。 return; }
んー、いまいちだな、結局、CallBackクラスの中にあるFunc呼ぶとこをインタフェースに応じて書き換えなきゃならんからこれではもう一つ有り難みがないのう。。
はい。というわけで、このように、関数もしくは関数オブジェクトといったいわゆる、「呼べるもの」、を、オブジェクトとして格納できるという概念は、boost::function というものがすでにあるわけです。boost::functionなら、関数のインタフェース(まー、ポリシーだよね)も任意のものを指定できます。(上記コード例のものはポリシー固定でしたが)なのでこういう場合は極力boost::functionを使いましょう、boost::(というか、最近ではtr1::ですか)が使えない場合はこういう道もあるよということで。はい。