自前仮想関数?
boostのSerializeで基底ポインタが渡されたときに、ちゃんと派生クラスの関数がよばれる。それも仮想関数じゃないのに。
で、その辺はRTTIをつかってゴリゴリやってるんだけど勉強がてら真似てみる。
ちなみにこの辺の仕事をこなしてるのはSerializationのvoid_casterとかbasic_objectのあたりなので興味あるかたはどぞ。
さて・・出来上がった汚いそーす。
#include "stdafx.h" #include <Loki/TypeInfo.h>//loki #include <iostream> #include <map> using namespace std; using namespace Loki; struct InvokerBase { virtual void Invoke(void* pv)=0; static map<TypeInfo,InvokerBase*> InvokeMap; }; map<TypeInfo,InvokerBase*> InvokerBase::InvokeMap; template <typename Base,typename Derived> struct Invoker:public InvokerBase { virtual void Invoke(void* pv) { (dynamic_cast<Derived*>(static_cast<Base*>(pv)))->f(); } }; template <typename Base> void FInvoker(Base* pd) { InvokerBase::InvokeMap[TypeInfo(typeid(*pd))]->Invoke(static_cast<void*>(pd)); } template <typename Base,typename Derived> struct Register { Register() { SelfRegist(); } void SelfRegist() { InvokerBase::InvokeMap.insert(make_pair(TypeInfo(typeid(Derived)),&InvokerInstance)); } static Invoker<Base,Derived> InvokerInstance; }; template <typename Base,typename Derived> Invoker<Base,Derived> Register<Base,Derived>::InvokerInstance; template <typename Base,typename Derived> struct RegisterInstanciater { static Register<Base,Derived> Instance1; static Register<Base,Derived>& Instance() { return Instance1; } }; template <typename Base,typename Derived> Register<Base,Derived> RegisterInstanciater<Base,Derived>::Instance1; template <typename Base,typename Derived> void BaseObject(Derived* =0,Base* =0) { RegisterInstanciater<Base,Derived>::Instance(); }; struct SuperBase { virtual ~SuperBase(){}; }; struct A:public SuperBase { void f() { BaseObject<SuperBase>(this); cout << "A" << endl; }; }; int _tmain(int argc, _TCHAR* argv[]) { A a; SuperBase* psb=&a; FInvoker(psb); return 0; }
いや。実はこれじゃぜんぜんだめなんです。
一応それらしくAと表示されますが、C++には多重継承とかあるので一つのTypeInfoを複数登録することができないと。
それにAクラスを継承するBクラスを考えるとBクラスのfに記述されるのは
BaseObject<SuperBase>(this)
じゃなくて
BaseObject<A>(this)
なはず
なのでSuperBase* -> A* -> B* というキャストをしなくちゃいけなくなる。
元ネタのboostだとこの辺をvoid_casterってのがやってるよーで。つまり、SuperBase*からB*へキャストしてvoid*にして返す。
で、ずっとvoid*で引き回した後にTypeInfo(D)で取得したpointer_oserializerが実行時型にstatic_castでキャストするっていう二段構えになってます。
こんなことをやるくらいなら素直に仮想関数ににして、それを前提にInvokeしたほうがもちろん全然早い。
じゃ、何でこんなことやってるのかというとシリアライズの処理を定義するためのSerialize関数がテンプレート。
テンプレート仮想関数とかあったらいいのに。(とあっても使いこなせないくせに言ってみる)