テンプレートクラス+関数ポインタ
C++で、結構ややこしかったので備忘録。
問題は、「テンプレートクラスに自分のクラスのメソッドを実行させる」ということ。
例えば、クラスWrapをテンプレートクラスとして、クラスAやクラスBのメンバを持つとする。
で、このテンプレートクラスからメインのクラスのメソッドを呼びたい。
まぁ、結論から言えば、以下の通りで実現可能。
#include <iostream> using namespace std; class A { public: int ia; A(){ ia = 0; }; }; class B { public: float fb; B(){ fb = 0.0f; }; }; template <class T, class M> class Wrap { private: M *parent; T content; void (M::*funcpointer)(T& ); public: Wrap(M *prt, void (M::*infp)(T& )) : parent(prt), funcpointer(infp) {}; void executeR(){ (parent->*funcpointer)(content); }; T getCont(){ return content; }; }; class MainClass { private: int privateValue; Wrap<A, MainClass> *rpA; Wrap<B, MainClass> *rpB; void operationA(A& classA){classA.ia += privateValue; }; void operationB(B& classB){classB.fb += (float)(privateValue)/10.0f; }; public: MainClass(){ privateValue = 5; void (MainClass::*fpointA)(A& ) = &MainClass::operationA; rpA = new Wrap<A, MainClass>(this, fpointA); rpB = new Wrap<B, MainClass>(this, &MainClass::operationB); }; void execute(){ rpA->executeR(); rpB->executeR(); }; void testPrint(){ cout << (rpA->getCont()).ia << ", " << (rpB->getCont()).fb << endl; }; }; int main() { MainClass mc; for(int i=0; i<10; i++) { mc.execute(); mc.testPrint(); } return(0); }
注意すべきは、staticなメンバの関数ポインタではないので、クラスを明示的に指定してやる必要があるということと、それにともなって呼び出し時にはその関数ポインタの指すメソッドのクラスのポインタからメソッドを実行する様に書く必要があるということ。
つまり、わざわざクラスWrapのテンプレート引数にクラスMainClassを指定して、さらに引数にそのMainClassクラスのポインタまで渡している。
結局のところ、これで何がしたいのかというと、たとえばWrapクラスがスレッドクラスだったら、と言う話である。つまり、Wrapクラスをスレッドクラスにしておいて、さらにキュー構造を持たしておき、そのキューのデータを毎ループで処理する関数を、メイン側から指定できるというわけである。
スレッドクラスの場合は、完全にWrapクラス内で完結しなければならず(した方がいい)、かと言ってそうすれば、処理の内容によってはいろんな変数をWrapクラスに流し込んでやる必要があり、それにより汎用性がなくなって、似たようなスレッドクラスを複数作るときは、その数だけコードを書かなければいけなくなる。
そういうわけで、こういうのがテンプレートらしい書き方…なんだろうか??
とはいえ、記述がややこしすぎる^^;
もっと簡単に書けないもんなのかね…
#そういやスレッドクラスを継承という手もあったけど、例えばOpenCVのVideoWriterとかを使うためにいちいちインクルードしてメンバに持たせるのもなぁ…とも思うが、むしろそうするべきなんだろうか?
#とりあえず自分なりの結論としては、実行するメソッドの中で使用する他のクラスの変数がやたら多かったり、プライベートなものを使いたかったりする場合は関数ポインタで、そうでない場合は継承という感じかな。。おそらく継承の方がカプセル化はできてるので、むしろ継承するようにコードを書いた方がいいような気もしてきた。
追記:むしろ普通に継承すべきだな。こういう書き方はかなり変態的な構文と思われる。