lldoubledispatch.h
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:15k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /**
  2.  * @file   lldoubledispatch.h
  3.  * @author Nat Goodspeed
  4.  * @date   2008-11-11
  5.  * @brief  function calls virtual on more than one parameter
  6.  * 
  7.  * $LicenseInfo:firstyear=2008&license=viewergpl$
  8.  * 
  9.  * Copyright (c) 2008-2010, Linden Research, Inc.
  10.  * 
  11.  * Second Life Viewer Source Code
  12.  * The source code in this file ("Source Code") is provided by Linden Lab
  13.  * to you under the terms of the GNU General Public License, version 2.0
  14.  * ("GPL"), unless you have obtained a separate licensing agreement
  15.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  16.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  17.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  18.  * 
  19.  * There are special exceptions to the terms and conditions of the GPL as
  20.  * it is applied to this Source Code. View the full text of the exception
  21.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  22.  * online at
  23.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  24.  * 
  25.  * By copying, modifying or distributing this software, you acknowledge
  26.  * that you have read and understood your obligations described above,
  27.  * and agree to abide by those obligations.
  28.  * 
  29.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  30.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  31.  * COMPLETENESS OR PERFORMANCE.
  32.  * $/LicenseInfo$
  33.  */
  34. #if ! defined(LL_LLDOUBLEDISPATCH_H)
  35. #define LL_LLDOUBLEDISPATCH_H
  36. #include <list>
  37. #include <boost/shared_ptr.hpp>
  38. #include <boost/function.hpp>
  39. #include <boost/bind.hpp>
  40. #include <boost/ref.hpp>
  41. /**
  42.  * This class supports function calls which are virtual on the dynamic type of
  43.  * more than one parameter. Specifically, we address a limited but useful
  44.  * subset of that problem: function calls which accept two parameters, and
  45.  * select which particular function to call depending on the dynamic type of
  46.  * both.
  47.  * 
  48.  * Scott Meyers, in More Effective C++ (Item 31), talks about some of the perils
  49.  * and pitfalls lurking down this pathway.  He discusses and dismisses the
  50.  * straightforward approaches of using single-dispatch virtual functions twice,
  51.  * and of using a family of single-dispatch virtual functions which each examine
  52.  * RTTI for their other parameter.  He advocates using a registry in which you
  53.  * look up the actual types of both parameters (he uses the classes' string names,
  54.  * via typeid(param).name()) to obtain a pointer to a free (non-member) function
  55.  * that will accept this pair of parameters.
  56.  * 
  57.  * He does point out that his approach doesn't handle inheritance.  If you have a
  58.  * registry entry for SpaceShip, and you have in hand a MilitaryShip (subclass of
  59.  * SpaceShip) and an Asteroid, you'd like to call the function appropriate for
  60.  * SpaceShips and Asteroids -- but alas, his lookup fails because the class name
  61.  * for your MilitaryShip subclass isn't in the registry.
  62.  * 
  63.  * This class extends his idea to build a registry whose entries can examine the
  64.  * dynamic type of the parameter in a more flexible way -- using dynamic_cast<>
  65.  * -- thereby supporting inheritance relationships.
  66.  * 
  67.  * Of course we must allow for the ambiguity this permits. We choose to use a
  68.  * sequence container rather than a map, and require that the client code
  69.  * specify the order in which dispatch-table entries should be searched. The
  70.  * result resembles the semantics of the catch clauses for a try/catch block:
  71.  * you must code catch clauses in decreasing order of specificity, because if
  72.  * you catch ErrorBaseClass before you catch ErrorSubclass, then any
  73.  * ErrorSubclass exceptions thrown by the protected code will always match
  74.  * ErrorBaseClass, and you will never reach your catch(ErrorSubclass) clause.
  75.  * 
  76.  * So, in a similar way, if you have a specific routine to process
  77.  * MilitaryShip and Asteroid, you'd better place that in the table @em before
  78.  * your more general routine that processes SpaceShip and Asteroid, or else
  79.  * the MilitaryShip variant will never be called.
  80.  *
  81.  * @todo This implementation doesn't attempt to deal with
  82.  * <tt>const</tt>-correctness of arguments. Our container stores templated
  83.  * objects into which the specific parameter types have been "frozen." But to
  84.  * store all these in the same container, they are all instances of a base
  85.  * class with a virtual invocation method. Naturally the base-class virtual
  86.  * method definition cannot know the <tt>const</tt>-ness of the particular
  87.  * types with which its template subclass is instantiated.
  88.  *
  89.  * One is tempted to suggest four different virtual methods, one for each
  90.  * combination of @c const and non-<tt>const</tt> arguments. Then the client
  91.  * will select the particular virtual method that best fits the
  92.  * <tt>const</tt>-ness of the arguments in hand. The trouble with that idea is
  93.  * that in order to instantiate the subclass instance, we must compile all
  94.  * four of its virtual method overrides, which means we must be prepared to
  95.  * pass all four combinations of @c const and non-<tt>const</tt> arguments to
  96.  * the registered callable. That only works when the registered callable
  97.  * accepts both parameters as @c const.
  98.  *
  99.  * Of course the virtual method overrides could use @c const_cast to force
  100.  * them to compile correctly regardless of the <tt>const</tt>-ness of the
  101.  * registered callable's parameter declarations. But if we're going to force
  102.  * the issue with @c const_cast anyway, why bother with the four different
  103.  * virtual methods? Why not just require canonical parameter
  104.  * <tt>const</tt>-ness for any callables used with this mechanism?
  105.  *
  106.  * We therefore require that your callable accept both params as
  107.  * non-<tt>const</tt>. (This is more general than @c const: you can perform @c
  108.  * const operations on a non-<tt>const</tt> parameter, but you can't perform
  109.  * non-<tt>const</tt> operations on a @c const parameter.)
  110.  *
  111.  * For ease of use, though, our invocation method accepts both params as @c
  112.  * const. Again, you can pass a non-<tt>const</tt> object to a @c const param,
  113.  * but not the other way around. We take care of the @c const_cast for you.
  114.  */
  115. // LLDoubleDispatch is a template because we have to assume that all parameter
  116. // types are subclasses of some common base class -- but we don't have to
  117. // impose that base class on client code.  Instead, we let IT tell US the
  118. // common parameter base class.
  119. template<class ReturnType, class ParamBaseType>
  120. class LLDoubleDispatch
  121. {
  122.     typedef LLDoubleDispatch<ReturnType, ParamBaseType> self_type;
  123. public:
  124.     LLDoubleDispatch() {}
  125.     /**
  126.      * Call the first matching entry.  If there's no registered Functor
  127.      * appropriate for this pair of parameter types, this call will do
  128.      * @em nothing!  (If you want notification in this case, simply add a new
  129.      * Functor for (ParamBaseType&, ParamBaseType&) at the end of the table.
  130.      * The two base-class entries should match anything that isn't matched by
  131.      * any more specific entry.)
  132.      *
  133.      * See note in class documentation about <tt>const</tt>-correctness.
  134.      */
  135.     inline
  136.     ReturnType operator()(const ParamBaseType& param1, const ParamBaseType& param2) const;
  137.     // Borrow a trick from Alexandrescu:  use a Type class to "wrap" a type
  138.     // for purposes of passing the type itself into a template method.
  139.     template<typename T>
  140.     struct Type {};
  141.     /**
  142.      * Add a new Entry for a given @a Functor. As mentioned above, the order
  143.      * in which you add these entries is very important.
  144.      *
  145.      * If you want symmetrical entries -- that is, if a B and an A can call
  146.      * the same Functor as an A and a B -- then pass @c true for the last
  147.      * parameter, and we'll add a (B, A) entry as well as an (A, B) entry. But
  148.      * your @a Functor can still be written to expect exactly the pair of types
  149.      * you've explicitly specified, because the Entry with the reversed params
  150.      * will call a special thunk that swaps params before calling your @a
  151.      * Functor.
  152.      */
  153.     template<typename Type1, typename Type2, class Functor>
  154.     void add(const Type<Type1>& t1, const Type<Type2>& t2, Functor func, bool symmetrical=false)
  155.     {
  156.         insert(t1, t2, func);
  157.         if (symmetrical)
  158.         {
  159.             // Use boost::bind() to construct a param-swapping thunk. Don't
  160.             // forget to reverse the parameters too.
  161.             insert(t2, t1, boost::bind(func, _2, _1));
  162.         }
  163.     }
  164.     /**
  165.      * Add a new Entry for a given @a Functor, explicitly passing instances of
  166.      * the Functor's leaf param types to help us figure out where to insert.
  167.      * Because it can use RTTI, this add() method isn't order-sensitive like
  168.      * the other one.
  169.      *
  170.      * If you want symmetrical entries -- that is, if a B and an A can call
  171.      * the same Functor as an A and a B -- then pass @c true for the last
  172.      * parameter, and we'll add a (B, A) entry as well as an (A, B) entry. But
  173.      * your @a Functor can still be written to expect exactly the pair of types
  174.      * you've explicitly specified, because the Entry with the reversed params
  175.      * will call a special thunk that swaps params before calling your @a
  176.      * Functor.
  177.      */
  178.     template <typename Type1, typename Type2, class Functor>
  179.     void add(const Type1& prototype1, const Type2& prototype2, Functor func, bool symmetrical=false)
  180.     {
  181.         // Because we expect our caller to pass leaf param types, we can just
  182.         // perform an ordinary search to find the first matching iterator. If
  183.         // we find an existing Entry that matches both params, either the
  184.         // param types are the same, or the existing Entry uses the base class
  185.         // for one or both params, and the new Entry must precede that. Assume
  186.         // our client won't register two callables with exactly the SAME set
  187.         // of types; in that case we'll insert the new one before any earlier
  188.         // ones, meaning the last one registered will "win." Note that if
  189.         // find() doesn't find any matching Entry, it will return end(),
  190.         // meaning the new Entry will be last, which is fine.
  191.         typename DispatchTable::iterator insertion = find(prototype1, prototype2);
  192.         insert(Type<Type1>(), Type<Type2>(), func, insertion);
  193.         if (symmetrical)
  194.         {
  195.             insert(Type<Type2>(), Type<Type1>(), boost::bind(func, _2, _1), insertion);
  196.         }
  197.     }
  198.     /**
  199.      * Add a new Entry for a given @a Functor, specifying the Functor's leaf
  200.      * param types as explicit template arguments. This will instantiate
  201.      * temporary objects of each of these types, which requires that they have
  202.      * a lightweight default constructor.
  203.      *
  204.      * If you want symmetrical entries -- that is, if a B and an A can call
  205.      * the same Functor as an A and a B -- then pass @c true for the last
  206.      * parameter, and we'll add a (B, A) entry as well as an (A, B) entry. But
  207.      * your @a Functor can still be written to expect exactly the pair of types
  208.      * you've explicitly specified, because the Entry with the reversed params
  209.      * will call a special thunk that swaps params before calling your @a
  210.      * Functor.
  211.      */
  212.     template <typename Type1, typename Type2, class Functor>
  213.     void add(Functor func, bool symmetrical=false)
  214.     {
  215.         // This is a convenience wrapper for the add() variant taking explicit
  216.         // instances.
  217.         add(Type1(), Type2(), func, symmetrical);
  218.     }
  219. private:
  220.     /// This is the base class for each entry in our dispatch table.
  221.     struct EntryBase
  222.     {
  223.         virtual ~EntryBase() {}
  224.         virtual bool matches(const ParamBaseType& param1, const ParamBaseType& param2) const = 0;
  225.         virtual ReturnType operator()(ParamBaseType& param1, ParamBaseType& param2) const = 0;
  226.     };
  227.     /// Here's the template subclass that actually creates each entry.
  228.     template<typename Type1, typename Type2, class Functor>
  229.     class Entry: public EntryBase
  230.     {
  231.     public:
  232.         Entry(Functor func): mFunc(func) {}
  233.         /// Is this entry appropriate for these arguments?
  234.         virtual bool matches(const ParamBaseType& param1, const ParamBaseType& param2) const
  235.         {
  236.             return (dynamic_cast<const Type1*>(&param1) &&
  237.                     dynamic_cast<const Type2*>(&param2));
  238.         }
  239.         /// invocation
  240.         virtual ReturnType operator()(ParamBaseType& param1, ParamBaseType& param2) const
  241.         {
  242.             // We perform the downcast so callable can accept leaf param
  243.             // types, instead of accepting ParamBaseType and downcasting
  244.             // explicitly.
  245.             return mFunc(dynamic_cast<Type1&>(param1), dynamic_cast<Type2&>(param2));
  246.         }
  247.     private:
  248.         /// Bind whatever function or function object the instantiator passed.
  249.         Functor mFunc;
  250.     };
  251.     /// shared_ptr manages Entry lifespan for us
  252.     typedef boost::shared_ptr<EntryBase> EntryPtr;
  253.     /// use a @c list to make it easy to insert
  254.     typedef std::list<EntryPtr> DispatchTable;
  255.     DispatchTable mDispatch;
  256.     /// Look up the location of the first matching entry.
  257.     typename DispatchTable::const_iterator find(const ParamBaseType& param1, const ParamBaseType& param2) const
  258.     {
  259.         // We assert that it's safe to call the non-const find() method on a
  260.         // const LLDoubleDispatch instance. Cast away the const-ness of 'this'.
  261.         return const_cast<self_type*>(this)->find(param1, param2);
  262.     }
  263.     /// Look up the location of the first matching entry.
  264.     typename DispatchTable::iterator find(const ParamBaseType& param1, const ParamBaseType& param2)
  265.     {
  266.         return std::find_if(mDispatch.begin(), mDispatch.end(),
  267.                             boost::bind(&EntryBase::matches, _1,
  268.                                         boost::ref(param1), boost::ref(param2)));
  269.     }
  270.     /// Look up the first matching entry.
  271.     EntryPtr lookup(const ParamBaseType& param1, const ParamBaseType& param2) const
  272.     {
  273.         typename DispatchTable::const_iterator found = find(param1, param2);            
  274.         if (found != mDispatch.end())
  275.         {
  276.             // Dereferencing the list iterator gets us an EntryPtr
  277.             return *found;
  278.         }
  279.         // not found
  280.         return EntryPtr();
  281.     }
  282.     // Break out the actual insert operation so the public add() template
  283.     // function can avoid calling itself recursively.  See add() comments.
  284.     template<typename Type1, typename Type2, class Functor>
  285.     void insert(const Type<Type1>& param1, const Type<Type2>& param2, Functor func)
  286.     {
  287.         insert(param1, param2, func, mDispatch.end());
  288.     }
  289.     // Break out the actual insert operation so the public add() template
  290.     // function can avoid calling itself recursively.  See add() comments.
  291.     template<typename Type1, typename Type2, class Functor>
  292.     void insert(const Type<Type1>&, const Type<Type2>&, Functor func,
  293.                 typename DispatchTable::iterator where)
  294.     {
  295.         mDispatch.insert(where, EntryPtr(new Entry<Type1, Type2, Functor>(func)));
  296.     }
  297.     /// Don't implement the copy ctor.  Everyone will be happier if the
  298.     /// LLDoubleDispatch object isn't copied.
  299.     LLDoubleDispatch(const LLDoubleDispatch& src);
  300. };
  301. template <class ReturnType, class ParamBaseType>
  302. ReturnType LLDoubleDispatch<ReturnType, ParamBaseType>::operator()(const ParamBaseType& param1,
  303.                                                                    const ParamBaseType& param2) const
  304. {
  305.     EntryPtr found = lookup(param1, param2);
  306.     if (found.get() == 0)
  307.         return ReturnType();    // bogus return value
  308.     // otherwise, call the Functor we found
  309.     return (*found)(const_cast<ParamBaseType&>(param1), const_cast<ParamBaseType&>(param2));
  310. }
  311. #endif /* ! defined(LL_LLDOUBLEDISPATCH_H) */