运行阶段双分派

如果你想在运行阶段分派,你将避免暴力手段,因为简单的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_;
}
;

模板化的双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  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);

这已经足够好了,但是还是有问题,你只能对已注册的类型进行操作。对于继承的情况,这需要客户转型,这是非常危险和我们不愿看到的,于是有了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));
            
//这太美妙了
    }

}
;

对于对称的问题,也不用在调用的时候进行判断,我们多提供一个方法就可以了:

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);
    }

}

我把这个理解为双分派的map解决方案,你也可以直接使用Loki库享用这个功能,但是了解这底层的细节有助于你处理类似问题的时候获得一个想法,而不是使用现成的东西。Loki给了你一个基本的框架可以使用模仿,但是遇到问题借助它的思想解决问题的情况也非常或者说更普遍,这是我现在使用Loki的感觉,所以更多的时候我想多了解一点这底层的东西,Andrei大脑里的东西。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值