//////////////////////////////////////////////////////////////////////////////// // 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-Wesley 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: August 9, 2002 #ifndef MULTIMETHODS_INC_ #define MULTIMETHODS_INC_ #include "Typelist.h" #include "Loki_TypeInfo.h" //### BCB #include "Functor.h" #include "AssocVector.h" #include // *** //////////////////////////////////////////////////////////////////////////////// // 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 { /* ### BCB checks existence Fire(rhs, lhs) even when this function is not needed, fails to compile 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); } }; */ template struct NormalInvocation { static ResultType DoDispatch(SomeLhs& lhs, SomeRhs& rhs, Executor& exec) { return exec.Fire(lhs, rhs); } }; template struct SwappedInvocation { static ResultType DoDispatch(SomeLhs& lhs, SomeRhs& rhs, Executor& exec) { 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, TList) { typedef typename TList::Head Head; typedef typename TList::Tail Tail; if (Head* p2 = dynamic_cast(&rhs)) { //### BCB - original statement was too complex for this compiler enum { val1 = int(TL::IndexOf::value) }; enum { val2 = int(TL::IndexOf::value) }; enum { val3 = symmetric && (val1 < val2) }; // BCB doesn't properly converts enum to bool enum { val4 = val3 != 0 }; const bool val5 = (val4 == 0) ? false : true; // it must be so clumsy typedef Private::NormalInvocation t1; typedef Private::SwappedInvocation t2; typedef Select::Result invocation_t; return invocation_t::DoDispatch(lhs, *p2, exec); } 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, TList) { typedef typename TList::Head Head; typedef typename TList::Tail Tail; 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()) { std::cout << "Function not found\n"; // 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 //### BCB - here it was probably buggy void Add(ResultType (*pFun)(SomeLhs&, SomeRhs&)) { return backEnd_.Add((ResultType (*)(BaseLhs&, BaseRhs&))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; //### BCB - this causes compiler crash (even when only one parameter is used) 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!!! //////////////////////////////////////////////////////////////////////////////// #endif