如果你想在运行阶段分派,你将避免暴力手段,因为简单的dynamic_cast底层一样消耗我们的效率。
pair是个不错的东西,它可以把两个东西当成一个用。pair两个类型作为索引可以获得双分派的目标函数,这是最朴素的想法:
template
<
class
BaseLhs,
class
BaseRhs,typename ResultType
>
class BasicDispatcher
... {
typedef pair<OrderedTypeInfo,OrderedTypeInfo> KeyType;
//pair是一个强有力的工具
typedef ResultType (*CallbackType)(BaseLhs&,BaseRhs&);
typedef CallbackType MappedType;
map<KeyType,MappedType> MapType;
MapType callbackMap_;
} ;
class BasicDispatcher
... {
typedef pair<OrderedTypeInfo,OrderedTypeInfo> KeyType;
//pair是一个强有力的工具
typedef ResultType (*CallbackType)(BaseLhs&,BaseRhs&);
typedef CallbackType MappedType;
map<KeyType,MappedType> MapType;
MapType callbackMap_;
} ;
模板化的双BasicDispatcher像这样:
template
<
class BaseLhs,
class BaseRhs,
typename ResultType,
typename CallbackType = ResultType ( * )(BaseLhs & ,BaseRhs & )
>
class BasicDispatcher
... {
typedef pair<TypeInfo,TypeInfo> KeyType;
typedef CallbackType MappedType;
typedef AssocVector<KeyType,MappedType> MapType;
MapType callbackMap_;
public:
template <class SomeLhs,class SomeRhs>
void Add(CallbackType fun)
...{
const KeyType key(typeid(SomeLhs),typeid(SomeRhs));
callbackMap_[key] = fun;
}
ResultType Go(BaseLhs& lhs,BaseRhs& rhs)
...{
MapType::iterator i = callbackMap_.find(KeyType(typeid(lhs),typeid(rhs)));
if (i == callbackMap_.end())
...{
throw "fun not found";
}
return (i->second)(lhs,rhs);
}
} ;
<
class BaseLhs,
class BaseRhs,
typename ResultType,
typename CallbackType = ResultType ( * )(BaseLhs & ,BaseRhs & )
>
class BasicDispatcher
... {
typedef pair<TypeInfo,TypeInfo> KeyType;
typedef CallbackType MappedType;
typedef AssocVector<KeyType,MappedType> MapType;
MapType callbackMap_;
public:
template <class SomeLhs,class SomeRhs>
void Add(CallbackType fun)
...{
const KeyType key(typeid(SomeLhs),typeid(SomeRhs));
callbackMap_[key] = fun;
}
ResultType Go(BaseLhs& lhs,BaseRhs& rhs)
...{
MapType::iterator i = callbackMap_.find(KeyType(typeid(lhs),typeid(rhs)));
if (i == callbackMap_.end())
...{
throw "fun not found";
}
return (i->second)(lhs,rhs);
}
} ;
使用起来像这样:
class
MyBase;
class MyA;
class MyB;
typedef BasicDispatcher < MyBase,MyBase, void > Dispatcher;
void DoSmThing(MyBase & lhs,MyBase rhs)
... {
MyA& ra = dynamic_cast<MyA&>(lhs);
MyB& rb = dynamic_cast<MyB&>(rhs);
//...
}
Dispatcher disp;
disp.Add < MyA,MyB > (DoSmThing);
class MyA;
class MyB;
typedef BasicDispatcher < MyBase,MyBase, void > Dispatcher;
void DoSmThing(MyBase & lhs,MyBase rhs)
... {
MyA& ra = dynamic_cast<MyA&>(lhs);
MyB& rb = dynamic_cast<MyB&>(rhs);
//...
}
Dispatcher disp;
disp.Add < MyA,MyB > (DoSmThing);
这已经足够好了,但是还是有问题,你只能对已注册的类型进行操作。对于继承的情况,这需要客户转型,这是非常危险和我们不愿看到的,于是有了trampoline的解决方案,它其实是对上述BasicDispatcher的一个包装,但是不需要用户处理转型。
template
<
class
BaseLhs,
class
BaseRhs,typename ResultType,
class
BDispatcher
=
BasicDispatcher
>
class FnDispatcher
... {
BDispatcher<BaseLhs,BaseRhs,ResultType> backEnd_;
public:
template <class ConcretLhs,class ConcretRhs,ResultType (*callback)(ConcretLhs&,ConcretRhs)>
void Add()
...{
struct Local
...{
static ResultType Trampoline(BaseLhs& lhs,BaseRhs& rhs)
...{
return callback(dynamic_cast<ConcretLhs&>(lhs),dynamic_cast<ConcretRhs&>(rhs));
}
};
return backEnd_.Add(ConcretRhs,ConcretLhs>(&Local::Trampoline));
//这太美妙了
}
} ;
class FnDispatcher
... {
BDispatcher<BaseLhs,BaseRhs,ResultType> backEnd_;
public:
template <class ConcretLhs,class ConcretRhs,ResultType (*callback)(ConcretLhs&,ConcretRhs)>
void Add()
...{
struct Local
...{
static ResultType Trampoline(BaseLhs& lhs,BaseRhs& rhs)
...{
return callback(dynamic_cast<ConcretLhs&>(lhs),dynamic_cast<ConcretRhs&>(rhs));
}
};
return backEnd_.Add(ConcretRhs,ConcretLhs>(&Local::Trampoline));
//这太美妙了
}
} ;
对于对称的问题,也不用在调用的时候进行判断,我们多提供一个方法就可以了:
template
<
class
ConcretLhs,
class
ConcretRhs,ResultType (
*
callback)(ConcretLhs
&
,ConcretRhs),
bool
symmetric
>
void Add()
... {
struct Local
...{
static ResultType Trampoline(BaseLhs& lhs,BaseRhs& rhs)
...{
return callback(dynamic_cast<ConcretLhs&>(lhs),dynamic_cast<ConcretRhs&>(rhs));
}
};
Add<ConcretLhs,ConcretRhs>(&Local::Trampoline);
if (symmetric)
...{
Add<ConcretRhs,ConcretLhs>(&Local::Trampoline);
}
}
void Add()
... {
struct Local
...{
static ResultType Trampoline(BaseLhs& lhs,BaseRhs& rhs)
...{
return callback(dynamic_cast<ConcretLhs&>(lhs),dynamic_cast<ConcretRhs&>(rhs));
}
};
Add<ConcretLhs,ConcretRhs>(&Local::Trampoline);
if (symmetric)
...{
Add<ConcretRhs,ConcretLhs>(&Local::Trampoline);
}
}
我把这个理解为双分派的map解决方案,你也可以直接使用Loki库享用这个功能,但是了解这底层的细节有助于你处理类似问题的时候获得一个想法,而不是使用现成的东西。Loki给了你一个基本的框架可以使用模仿,但是遇到问题借助它的思想解决问题的情况也非常或者说更普遍,这是我现在使用Loki的感觉,所以更多的时候我想多了解一点这底层的东西,Andrei大脑里的东西。