modern c++ design 第十一章

 本章介绍了经常遇到的双分派的一种泛型解决方案。 C++在语法上实现了单分派, 即虚函数, 通过动态机制选择相应的函数。 双分派是形如fun(Object1* a, Object2* b). 根据a和b的实际类型动态自动分派一个处理函数。
最容易想到的方案,蛮干法: 写一大堆重载函数. 不过这种方法会有很强的依赖性。 也提供了一种泛化蛮干法的实现: Executor是实现了各种具体实现的类, 其中实现了各种Fire函数. BaseLhs是左操作数的基类, TypesLhs是一个typelist, 包括了左操作数可能的类型集合. BaseRhs和TypesRhs类似,不过针对右操作数. symmetric是是否对称, 如果对称, 则需要实现的函数减半. ResultType是返回值类型,通常为void.
    template
    <
        class Executor,
        class BaseLhs,
        class TypesLhs,
        bool symmetric = true,
        class BaseRhs = BaseLhs,
        class TypesRhs = TypesLhs,
        typename ResultType = void
    >
    class StaticDispatcher
    {
        template <class SomeLhs>
        static ResultType DispatchRhs(SomeLhs& lhs, BaseRhs& rhs,
            Executor exec, NullType)
        { return exec.OnError(lhs, rhs); }
       
        template <class Head, class Tail, class SomeLhs>
        static ResultType DispatchRhs(SomeLhs& lhs, BaseRhs& rhs,
            Executor exec, Typelist<Head, Tail>)
        {           
            if (Head* p2 = dynamic_cast<Head*>(&rhs))
            {
                Int2Type<(symmetric &&
                          int(TL::IndexOf<TypesRhs, Head>::value) <
                          int(TL::IndexOf<TypesLhs, SomeLhs>::value))> i2t;

                typedef Private::InvocationTraits<
                        SomeLhs, Head, Executor, ResultType> CallTraits;
                   
                return CallTraits::DoDispatch(lhs, *p2, exec, i2t);
            }
            return DispatchRhs(lhs, rhs, exec, Tail());
        }
       
        static ResultType DispatchLhs(BaseLhs& lhs, BaseRhs& rhs,
            Executor exec, NullType)
        { return exec.OnError(lhs, rhs); }
       
        template <class Head, class Tail>
        static ResultType DispatchLhs(BaseLhs& lhs, BaseRhs& rhs,
            Executor exec, Typelist<Head, Tail>)
        {           
            if (Head* p1 = dynamic_cast<Head*>(&lhs))
            {
                return DispatchRhs(*p1, rhs, exec, TypesRhs());
            }
            return DispatchLhs(lhs, rhs, exec, Tail());
        }

    public:
        static ResultType Go(BaseLhs& lhs, BaseRhs& rhs,
            Executor exec)
        { return DispatchLhs(lhs, rhs, exec, TypesLhs()); }
    };
实现方式还是模板元编程的方法, 利用静态递归实现. 如果对第三章内容有相当的理解,读懂这段也非常简单了.
symmetric是使用bool的模板变量的分派实现的.  利用了一个辅助类InvocationTraits. 从辅助类的使用可以看到, 不同的职责都分派给了不同的类去实现, 耦合性非常低,利于重用.
        template <class SomeLhs, class SomeRhs,
            class Executor, typename ResultType>
        struct InvocationTraits
        {
            static ResultType
        DoDispatch(SomeLhs& lhs, SomeRhs& rhs,
            Executor& exec, Int2Type<false>)
            {
                return exec.Fire(lhs, rhs);
            }
            static ResultType
        DoDispatch(SomeLhs& lhs, SomeRhs& rhs,
            Executor& exec, Int2Type<true>)
            {
                return exec.Fire(rhs, lhs);
            }
        };


由于暴力法的依存性以及查找性能, 只有在类很少时使用.  必须考虑其他的实现
对数型分派.  依赖type_info的before函数进行排序, 维护一个执行期map, 为不同的组合注册callback, 根据2分查找寻找适合的callback, 可以实现对数性能. 即O(lgn).  此实现考虑到转型的问题, 函数调用进行了一次包装. 这一段读的不是很明白.  目的是得到具体型别不丢失, 需要实际使用才能更好理解吧. 利用前面提供的Functor可以分派到泛函数. 实现:
   template
    <
        class BaseLhs,
        class BaseRhs = BaseLhs,
        typename ResultType = void,
        typename CallbackType = ResultType (*)(BaseLhs&, BaseRhs&)
    >
    class BasicDispatcher
    {
        typedef std::pair<TypeInfo,TypeInfo> KeyType;
        typedef CallbackType MappedType;
        typedef AssocVector<KeyType, MappedType> MapType;
        MapType callbackMap_;
       
        void DoAdd(TypeInfo lhs, TypeInfo rhs, CallbackType fun);
        bool DoRemove(TypeInfo lhs, TypeInfo rhs);
       
    public:
        template <class SomeLhs, class SomeRhs>
        void Add(CallbackType fun)
        {
            DoAdd(typeid(SomeLhs), typeid(SomeRhs), fun);
        }
       
        template <class SomeLhs, class SomeRhs>
        bool Remove()
        {
            return DoRemove(typeid(SomeLhs), typeid(SomeRhs));
        }
       
        ResultType Go(BaseLhs& lhs, BaseRhs& rhs);
    };

    // Non-inline to reduce compile time overhead...
    template <class BaseLhs, class BaseRhs,
        typename ResultType, typename CallbackType>
    void BasicDispatcher<BaseLhs,BaseRhs,ResultType,CallbackType>
         ::DoAdd(TypeInfo lhs, TypeInfo rhs, CallbackType fun)
    {
        callbackMap_[KeyType(lhs, rhs)] = fun;
    }
       
    template <class BaseLhs, class BaseRhs,
        typename ResultType, typename CallbackType>
    bool BasicDispatcher<BaseLhs,BaseRhs,ResultType,CallbackType>
         ::DoRemove(TypeInfo lhs, TypeInfo rhs)
    {
        return callbackMap_.erase(KeyType(lhs, rhs)) == 1;
    }

    template <class BaseLhs, class BaseRhs,
        typename ResultType, typename CallbackType>
    ResultType BasicDispatcher<BaseLhs,BaseRhs,ResultType,CallbackType>
               ::Go(BaseLhs& lhs, BaseRhs& rhs)
    {
        typename MapType::key_type k(typeid(lhs),typeid(rhs));
        typename MapType::iterator i = callbackMap_.find(k);
        if (i == callbackMap_.end())
        {
                throw std::runtime_error("Function not found");
        }
        return (i->second)(lhs, rhs);
    }
由于多继承的问题, static转型在菱形继承的时候不能使用, 而Dynamic的转型性能消耗较大. 所以loki提供了一个转型策略.
最后, 还有一个BasicFastDispatcher. 是常数期的分派方法.  实际是使用了数组方法解决的问题, 代价是每个类都需要包括一个宏, 这个宏定义了那个类在数组中的下标.

对我来说, 了解了这些概念足以,  暂时的应用中,对数法或者暴力法可能更为实用一些. 高阶内容留待需求的提升吧. 不过对于思想的提升, 这些概念是意义重大的.
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值