www.pudn.com > Loki.rar > MultiMethods.h


//////////////////////////////////////////////////////////////////////////////// 
// The Loki Library 
// Copyright (c) 2001 by Andrei Alexandrescu 
// This code accompanies the book: 
// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design  
//     Patterns Applied". Copyright (c) 2001. Addison-Wesley. 
// Permission to use, copy, modify, distribute and sell this software for any  
//     purpose is hereby granted without fee, provided that the above copyright  
//     notice appear in all copies and that both that copyright notice and this  
//     permission notice appear in supporting documentation. 
// The author or Addison-Welsey Longman make no representations about the  
//     suitability of this software for any purpose. It is provided "as is"  
//     without express or implied warranty. 
//////////////////////////////////////////////////////////////////////////////// 
 
// Last update: May 19, 2002 
 
#ifndef MULTIMETHODS_INC_ 
#define MULTIMETHODS_INC_ 
 
#include "Typelist.h" 
#include "LokiTypeInfo.h" 
#include "Functor.h" 
#include "AssocVector.h" 
 
//////////////////////////////////////////////////////////////////////////////// 
// IMPORTANT NOTE: 
// The double dispatchers implemented below differ from the excerpts shown in 
// the book - they are simpler while respecting the same interface. 
//////////////////////////////////////////////////////////////////////////////// 
 
namespace Loki 
{ 
//////////////////////////////////////////////////////////////////////////////// 
// class template InvocationTraits (helper) 
// Helps implementing optional symmetry 
//////////////////////////////////////////////////////////////////////////////// 
 
    namespace Private 
    { 
        template  
        struct InvocationTraits 
        { 
            static ResultType  
        DoDispatch(SomeLhs& lhs, SomeRhs& rhs,  
            Executor& exec, Int2Type) 
            { 
                return exec.Fire(lhs, rhs); 
            } 
            static ResultType  
        DoDispatch(SomeLhs& lhs, SomeRhs& rhs,  
            Executor& exec, Int2Type) 
            { 
                return exec.Fire(rhs, lhs); 
            } 
        }; 
    } 
 
//////////////////////////////////////////////////////////////////////////////// 
// class template StaticDispatcher 
// Implements an automatic static double dispatcher based on two typelists 
//////////////////////////////////////////////////////////////////////////////// 
 
    template 
    < 
        class Executor, 
        class BaseLhs,  
        class TypesLhs, 
        bool symmetric = true, 
        class BaseRhs = BaseLhs, 
        class TypesRhs = TypesLhs, 
        typename ResultType = void 
    > 
    class StaticDispatcher 
    { 
        template  
        static ResultType DispatchRhs(SomeLhs& lhs, BaseRhs& rhs, 
            Executor exec, NullType) 
        { return exec.OnError(lhs, rhs); } 
         
        template  
        static ResultType DispatchRhs(SomeLhs& lhs, BaseRhs& rhs, 
            Executor exec, Typelist) 
        {             
            if (Head* p2 = dynamic_cast(&rhs)) 
            { 
                Int2Type<(symmetric && 
                          int(TL::IndexOf::value) < 
                          int(TL::IndexOf::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  
        static ResultType DispatchLhs(BaseLhs& lhs, BaseRhs& rhs, 
            Executor exec, Typelist) 
        {             
            if (Head* p1 = dynamic_cast(&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()); } 
    }; 
     
//////////////////////////////////////////////////////////////////////////////// 
// class template BasicDispatcher 
// Implements a logarithmic double dispatcher for functors (or functions) 
// Doesn't offer automated casts or symmetry 
//////////////////////////////////////////////////////////////////////////////// 
 
    template 
    < 
        class BaseLhs, 
        class BaseRhs = BaseLhs, 
        typename ResultType = void, 
        typename CallbackType = ResultType (*)(BaseLhs&, BaseRhs&) 
    > 
    class BasicDispatcher 
    { 
        typedef std::pair KeyType; 
        typedef CallbackType MappedType; 
        typedef AssocVector MapType; 
        MapType callbackMap_; 
         
        void DoAdd(TypeInfo lhs, TypeInfo rhs, CallbackType fun); 
        bool DoRemove(TypeInfo lhs, TypeInfo rhs); 
         
    public: 
        template  
        void Add(CallbackType fun) 
        { 
            DoAdd(typeid(SomeLhs), typeid(SomeRhs), fun); 
        } 
         
        template  
        bool Remove() 
        { 
            return DoRemove(typeid(SomeLhs), typeid(SomeRhs)); 
        } 
         
        ResultType Go(BaseLhs& lhs, BaseRhs& rhs); 
    }; 
 
    // Non-inline to reduce compile time overhead... 
    template  
    void BasicDispatcher 
         ::DoAdd(TypeInfo lhs, TypeInfo rhs, CallbackType fun) 
    { 
        callbackMap_[KeyType(lhs, rhs)] = fun; 
    } 
         
    template  
    bool BasicDispatcher 
         ::DoRemove(TypeInfo lhs, TypeInfo rhs) 
    { 
        return callbackMap_.erase(KeyType(lhs, rhs)) == 1; 
    } 
 
    template  
    ResultType BasicDispatcher 
               ::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); 
    } 
 
//////////////////////////////////////////////////////////////////////////////// 
// class template StaticCaster 
// Implementation of the CastingPolicy used by FunctorDispatcher 
//////////////////////////////////////////////////////////////////////////////// 
 
    template  
    struct StaticCaster 
    { 
        static To& Cast(From& obj) 
        { 
            return static_cast(obj); 
        } 
    }; 
 
//////////////////////////////////////////////////////////////////////////////// 
// class template DynamicCaster 
// Implementation of the CastingPolicy used by FunctorDispatcher 
//////////////////////////////////////////////////////////////////////////////// 
 
    template  
    struct DynamicCaster 
    { 
        static To& Cast(From& obj) 
        { 
            return dynamic_cast(obj); 
        } 
    }; 
 
//////////////////////////////////////////////////////////////////////////////// 
// class template Private::FnDispatcherHelper 
// Implements trampolines and argument swapping used by FnDispatcher 
//////////////////////////////////////////////////////////////////////////////// 
 
    namespace Private 
    { 
        template  
        struct FnDispatcherHelper 
        { 
            static ResultType Trampoline(BaseLhs& lhs, BaseRhs& rhs) 
            { 
                return Callback(CastLhs::Cast(lhs), CastRhs::Cast(rhs)); 
            } 
            static ResultType TrampolineR(BaseRhs& rhs, BaseLhs& lhs) 
            { 
                return Trampoline(lhs, rhs); 
            } 
        }; 
    } 
 
//////////////////////////////////////////////////////////////////////////////// 
// class template FnDispatcher 
// Implements an automatic logarithmic double dispatcher for functions 
// Features automated conversions 
//////////////////////////////////////////////////////////////////////////////// 
 
    template  class CastingPolicy = DynamicCaster, 
              template  
              class DispatcherBackend = BasicDispatcher> 
    class FnDispatcher 
    { 
        DispatcherBackend backEnd_; 
         
    public: 
        template  
        void Add(ResultType (*pFun)(BaseLhs&, BaseRhs&)) 
        { 
            return backEnd_.Add(pFun); 
        }         
         
        template  
        void Add() 
        { 
        typedef Private::FnDispatcherHelper< 
                    BaseLhs, BaseRhs,  
                    SomeLhs, SomeRhs, 
                    ResultType, 
                    CastingPolicy,  
                    CastingPolicy,  
                    callback> Local; 
 
            Add(&Local::Trampoline); 
        } 
         
        template  
        void Add() 
        { 
        typedef Private::FnDispatcherHelper< 
                    BaseLhs, BaseRhs,  
                    SomeLhs, SomeRhs, 
                    ResultType, 
                    CastingPolicy,  
                    CastingPolicy,  
                    callback> Local; 
 
            Add(&Local::Trampoline); 
            if (symmetric) 
            { 
                Add(&Local::TrampolineR); 
            } 
        } 
         
        template  
        void Remove() 
        { 
            backEnd_.Remove(); 
        } 
 
        ResultType Go(BaseLhs& lhs, BaseRhs& rhs) 
        { 
            return backEnd_.Go(lhs, rhs); 
        } 
    }; 
 
//////////////////////////////////////////////////////////////////////////////// 
// class template FunctorDispatcherAdaptor 
// permits use of FunctorDispatcher under gcc.2.95.2/3 
/////////////////////////////////////////////////////////////////////////////// 
 
    namespace Private 
    { 
    template  
        class FunctorDispatcherHelper  
        { 
            Fun fun_; 
            ResultType Fire(BaseLhs& lhs, BaseRhs& rhs,Int2Type) 
            { 
                return fun_(CastLhs::Cast(lhs), CastRhs::Cast(rhs)); 
            } 
            ResultType Fire(BaseLhs& rhs, BaseRhs& lhs,Int2Type) 
            { 
                return fun_(CastLhs::Cast(lhs), CastRhs::Cast(rhs)); 
            } 
        public: 
            FunctorDispatcherHelper(const Fun& fun) : fun_(fun) {} 
 
            ResultType operator()(BaseLhs& lhs, BaseRhs& rhs) 
            { 
                return Fire(lhs,rhs,Int2Type()); 
            } 
        }; 
    } 
 
//////////////////////////////////////////////////////////////////////////////// 
// class template FunctorDispatcher 
// Implements a logarithmic double dispatcher for functors 
// Features automated casting 
//////////////////////////////////////////////////////////////////////////////// 
 
    template  class CastingPolicy = DynamicCaster,  
              template  
              class DispatcherBackend = BasicDispatcher> 
    class FunctorDispatcher 
    { 
        typedef TYPELIST_2(BaseLhs&, BaseRhs&) ArgsList; 
        typedef Functor FunctorType; 
 
        DispatcherBackend backEnd_; 
 
    public: 
        template  
        void Add(const Fun& fun) 
        { 
            typedef Private::FunctorDispatcherHelper< 
                    BaseLhs, BaseRhs, 
                    SomeLhs, SomeRhs, 
                    ResultType, 
                    CastingPolicy, 
                    CastingPolicy, 
                    Fun, false> Adapter; 
 
            backEnd_.Add(FunctorType(Adapter(fun))); 
    } 
        template  
        void Add(const Fun& fun) 
        { 
        Add(fun); 
 
        if (symmetric) 
        { 
        // Note: symmetry only makes sense where BaseLhs==BaseRhs 
                typedef Private::FunctorDispatcherHelper< 
                    BaseLhs, BaseLhs, 
                    SomeLhs, SomeRhs, 
                    ResultType, 
                    CastingPolicy, 
                    CastingPolicy, 
                    Fun, true> AdapterR; 
 
                backEnd_.Add(FunctorType(AdapterR(fun))); 
        } 
        } 
         
        template  
        void Remove() 
        { 
            backEnd_.Remove(); 
        } 
 
        ResultType Go(BaseLhs& lhs, BaseRhs& rhs) 
        { 
            return backEnd_.Go(lhs, rhs); 
        } 
    }; 
} // namespace Loki 
 
//////////////////////////////////////////////////////////////////////////////// 
// Change log: 
// June 20, 2001: ported by Nick Thurn to gcc 2.95.3. Kudos, Nick!!! 
// May  10, 2002: ported by Rani Sharoni to VC7 (RTM - 9466) 
//////////////////////////////////////////////////////////////////////////////// 
 
#endif