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

游戏引擎

开发平台:

C++ Builder

  1. /**
  2.  * @file   lllazy.h
  3.  * @author Nat Goodspeed
  4.  * @date   2009-01-22
  5.  * @brief  Lazy instantiation of specified type. Useful in conjunction with
  6.  *         Michael Feathers's "Extract and Override Getter" ("Working
  7.  *         Effectively with Legacy Code", p. 352).
  8.  *
  9.  * Quoting his synopsis of steps on p.355:
  10.  *
  11.  * 1. Identify the object you need a getter for.
  12.  * 2. Extract all of the logic needed to create the object into a getter.
  13.  * 3. Replace all uses of the object with calls to the getter, and initialize
  14.  *    the reference that holds the object to null in all constructors.
  15.  * 4. Add the first-time logic to the getter so that the object is constructed
  16.  *    and assigned to the reference whenever the reference is null.
  17.  * 5. Subclass the class and override the getter to provide an alternative
  18.  *    object for testing.
  19.  *
  20.  * It's the second half of bullet 3 (3b, as it were) that bothers me. I find
  21.  * it all too easy to imagine adding pointer initializers to all but one
  22.  * constructor... the one not exercised by my tests. That suggested using
  23.  * (e.g.) boost::scoped_ptr<MyObject> so you don't have to worry about
  24.  * destroying it either.
  25.  *
  26.  * However, introducing additional machinery allows us to encapsulate bullet 4
  27.  * as well.
  28.  *
  29.  * $LicenseInfo:firstyear=2009&license=viewergpl$
  30.  * 
  31.  * Copyright (c) 2009-2010, Linden Research, Inc.
  32.  * 
  33.  * Second Life Viewer Source Code
  34.  * The source code in this file ("Source Code") is provided by Linden Lab
  35.  * to you under the terms of the GNU General Public License, version 2.0
  36.  * ("GPL"), unless you have obtained a separate licensing agreement
  37.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  38.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  39.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  40.  * 
  41.  * There are special exceptions to the terms and conditions of the GPL as
  42.  * it is applied to this Source Code. View the full text of the exception
  43.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  44.  * online at
  45.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  46.  * 
  47.  * By copying, modifying or distributing this software, you acknowledge
  48.  * that you have read and understood your obligations described above,
  49.  * and agree to abide by those obligations.
  50.  * 
  51.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  52.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  53.  * COMPLETENESS OR PERFORMANCE.
  54.  * $/LicenseInfo$
  55.  */
  56. #if ! defined(LL_LLLAZY_H)
  57. #define LL_LLLAZY_H
  58. #include <boost/function.hpp>
  59. #include <boost/scoped_ptr.hpp>
  60. #include <boost/lambda/construct.hpp>
  61. #include <stdexcept>
  62. /// LLLazyCommon simply factors out of LLLazy<T> things that don't depend on
  63. /// its template parameter.
  64. class LLLazyCommon
  65. {
  66. public:
  67.     /**
  68.      * This exception is thrown if you try to replace an LLLazy<T>'s factory
  69.      * (or T* instance) after it already has an instance in hand. Since T
  70.      * might well be stateful, we can't know the effect of silently discarding
  71.      * and replacing an existing instance, so we disallow it. This facility is
  72.      * intended for testing, and in a test scenario we can definitely control
  73.      * that.
  74.      */
  75.     struct InstanceChange: public std::runtime_error
  76.     {
  77.         InstanceChange(const std::string& what): std::runtime_error(what) {}
  78.     };
  79. protected:
  80.     /**
  81.      * InstanceChange might be appropriate in a couple of different LLLazy<T>
  82.      * methods. Factor out the common logic.
  83.      */
  84.     template <typename PTR>
  85.     static void ensureNoInstance(const PTR& ptr)
  86.     {
  87.         if (ptr)
  88.         {
  89.             // Too late: we've already instantiated the lazy object. We don't
  90.             // know whether it's stateful or not, so it's not safe to discard
  91.             // the existing instance in favor of a replacement.
  92.             throw InstanceChange("Too late to replace LLLazy instance");
  93.         }
  94.     }
  95. };
  96. /**
  97.  * LLLazy<T> is useful when you have an outer class Outer that you're trying
  98.  * to bring under unit test, that contains a data member difficult to
  99.  * instantiate in a test harness. Typically the data member's class Inner has
  100.  * many thorny dependencies. Feathers generally advocates "Extract and
  101.  * Override Factory Method" (p. 350). But in C++, you can't call a derived
  102.  * class override of a virtual method from the derived class constructor,
  103.  * which limits applicability of "Extract and Override Factory Method." For
  104.  * such cases Feathers presents "Extract and Override Getter" (p. 352).
  105.  *
  106.  * So we'll assume that your class Outer contains a member like this:
  107.  * @code
  108.  * Inner mInner;
  109.  * @endcode
  110.  *
  111.  * LLLazy<Inner> can be used to replace this member. You can directly declare:
  112.  * @code
  113.  * LLLazy<Inner> mInner;
  114.  * @endcode
  115.  * and change references to mInner accordingly.
  116.  *
  117.  * (Alternatively, you can add a base class of the form
  118.  * <tt>LLLazyBase<Inner></tt>. This is discussed further in the LLLazyBase<T>
  119.  * documentation.)
  120.  *
  121.  * LLLazy<T> binds a <tt>boost::scoped_ptr<T></tt> and a factory functor
  122.  * returning T*. You can either bind that functor explicitly or let it default
  123.  * to the expression <tt>new T()</tt>.
  124.  *
  125.  * As long as LLLazy<T> remains unreferenced, its T remains uninstantiated.
  126.  * The first time you use get(), <tt>operator*()</tt> or <tt>operator->()</tt>
  127.  * it will instantiate its T and thereafter behave like a pointer to it.
  128.  *
  129.  * Thus, any existing reference to <tt>mInner.member</tt> should be replaced
  130.  * with <tt>mInner->member</tt>. Any simple reference to @c mInner should be
  131.  * replaced by <tt>*mInner</tt>.
  132.  *
  133.  * (If the original declaration was a pointer initialized in Outer's
  134.  * constructor, e.g. <tt>Inner* mInner</tt>, so much the better. In that case
  135.  * you should be able to drop in <tt>LLLazy<Inner></tt> without much change.)
  136.  *
  137.  * The support for "Extract and Override Getter" lies in the fact that you can
  138.  * replace the factory functor -- or provide an explicit T*. Presumably this
  139.  * is most useful from a test subclass -- which suggests that your @c mInner
  140.  * member should be @c protected.
  141.  *
  142.  * Note that <tt>boost::lambda::new_ptr<T>()</tt> makes a dandy factory
  143.  * functor, for either the set() method or LLLazy<T>'s constructor. If your T
  144.  * requires constructor arguments, use an expression more like
  145.  * <tt>boost::lambda::bind(boost::lambda::new_ptr<T>(), arg1, arg2, ...)</tt>.
  146.  *
  147.  * Of course the point of replacing the functor is to substitute a class that,
  148.  * though referenced as Inner*, is not an Inner; presumably this is a testing
  149.  * subclass of Inner (e.g. TestInner). Thus your test subclass TestOuter for
  150.  * the containing class Outer will contain something like this:
  151.  * @code
  152.  * class TestOuter: public Outer
  153.  * {
  154.  * public:
  155.  *     TestOuter()
  156.  *     {
  157.  *         // mInner must be 'protected' rather than 'private'
  158.  *         mInner.set(boost::lambda::new_ptr<TestInner>());
  159.  *     }
  160.  *     ...
  161.  * };
  162.  * @endcode
  163.  */
  164. template <typename T>
  165. class LLLazy: public LLLazyCommon
  166. {
  167. public:
  168.     /// Any nullary functor returning T* will work as a Factory
  169.     typedef boost::function<T* ()> Factory;
  170.     /// The default LLLazy constructor uses <tt>new T()</tt> as its Factory
  171.     LLLazy():
  172.         mFactory(boost::lambda::new_ptr<T>())
  173.     {}
  174.     /// Bind an explicit Factory functor
  175.     LLLazy(const Factory& factory):
  176.         mFactory(factory)
  177.     {}
  178.     /// Reference T, instantiating it if this is the first access
  179.     const T& get() const
  180.     {
  181.         if (! mInstance)
  182.         {
  183.             // use the bound Factory functor
  184.             mInstance.reset(mFactory());
  185.         }
  186.         return *mInstance;
  187.     }
  188.     /// non-const get()
  189.     T& get()
  190.     {
  191.         return const_cast<T&>(const_cast<const LLLazy<T>*>(this)->get());
  192.     }
  193.     /// operator*() is equivalent to get()
  194.     const T& operator*() const { return get(); }
  195.     /// operator*() is equivalent to get()
  196.     T& operator*() { return get(); }
  197.     /**
  198.      * operator->() must return (something resembling) T*. It's tempting to
  199.      * return the underlying boost::scoped_ptr<T>, but that would require
  200.      * breaking out the lazy-instantiation logic from get() into a common
  201.      * private method. Assume the pointer used for operator->() access is very
  202.      * short-lived.
  203.      */
  204.     const T* operator->() const { return &get(); }
  205.     /// non-const operator->()
  206.     T* operator->() { return &get(); }
  207.     /// set(Factory). This will throw InstanceChange if mInstance has already
  208.     /// been set.
  209.     void set(const Factory& factory)
  210.     {
  211.         ensureNoInstance(mInstance);
  212.         mFactory = factory;
  213.     }
  214.     /// set(T*). This will throw InstanceChange if mInstance has already been
  215.     /// set.
  216.     void set(T* instance)
  217.     {
  218.         ensureNoInstance(mInstance);
  219.         mInstance.reset(instance);
  220.     }
  221. private:
  222.     Factory mFactory;
  223.     // Consider an LLLazy<T> member of a class we're accessing by const
  224.     // reference. We want to allow even const methods to touch the LLLazy<T>
  225.     // member. Hence the actual pointer must be mutable because such access
  226.     // might assign it.
  227.     mutable boost::scoped_ptr<T> mInstance;
  228. };
  229. #if (! defined(__GNUC__)) || (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
  230. // Not gcc at all, or a gcc more recent than gcc 3.3
  231. #define GCC33 0
  232. #else
  233. #define GCC33 1
  234. #endif
  235. /**
  236.  * LLLazyBase<T> wraps LLLazy<T>, giving you an alternative way to replace
  237.  * <tt>Inner mInner;</tt>. Instead of coding <tt>LLLazy<Inner> mInner</tt>,
  238.  * you can add LLLazyBase<Inner> to your Outer class's bases, e.g.:
  239.  * @code
  240.  * class Outer: public LLLazyBase<Inner>
  241.  * {
  242.  *     ...
  243.  * };
  244.  * @endcode
  245.  *
  246.  * This gives you @c public get() and @c protected set() methods without
  247.  * having to make your LLLazy<Inner> member @c protected. The tradeoff is that
  248.  * you must access the wrapped LLLazy<Inner> using get() and set() rather than
  249.  * with <tt>operator*()</tt> or <tt>operator->()</tt>.
  250.  *
  251.  * This mechanism can be used for more than one member, but only if they're of
  252.  * different types. That is, you can replace:
  253.  * @code
  254.  * DifficultClass mDifficult;
  255.  * AwkwardType    mAwkward;
  256.  * @endcode
  257.  * with:
  258.  * @code
  259.  * class Outer: public LLLazyBase<DifficultClass>, public LLLazyBase<AwkwardType>
  260.  * {
  261.  *     ...
  262.  * };
  263.  * @endcode
  264.  * but for a situation like this:
  265.  * @code
  266.  * DifficultClass mMainDifficult, mAuxDifficult;
  267.  * @endcode
  268.  * you should directly embed LLLazy<DifficultClass> (q.v.).
  269.  *
  270.  * For multiple LLLazyBase bases, e.g. the <tt>LLLazyBase<DifficultClass>,
  271.  * LLLazyBase<AwkwardType></tt> example above, access the relevant get()/set()
  272.  * as (e.g.) <tt>LLLazyBase<DifficultClass>::get()</tt>. (This is why you
  273.  * can't have multiple LLLazyBase<T> of the same T.) For a bit of syntactic
  274.  * sugar, please see getLazy()/setLazy().
  275.  */
  276. template <typename T>
  277. class LLLazyBase
  278. {
  279. public:
  280.     /// invoke default LLLazy constructor
  281.     LLLazyBase() {}
  282.     /// make wrapped LLLazy bind an explicit Factory
  283.     LLLazyBase(const typename LLLazy<T>::Factory& factory):
  284.         mInstance(factory)
  285.     {}
  286.     /// access to LLLazy::get()
  287.     T& get() { return *mInstance; }
  288.     /// access to LLLazy::get()
  289.     const T& get() const { return *mInstance; }
  290. protected:
  291.     // see getLazy()/setLazy()
  292.     #if (! GCC33)
  293.     template <typename T2, class MYCLASS> friend T2& getLazy(MYCLASS* this_);
  294.     template <typename T2, class MYCLASS> friend const T2& getLazy(const MYCLASS* this_);
  295.     #else // gcc 3.3
  296.     template <typename T2, class MYCLASS> friend T2& getLazy(const MYCLASS* this_);
  297.     #endif // gcc 3.3
  298.     template <typename T2, class MYCLASS> friend void setLazy(MYCLASS* this_, T2* instance);
  299.     template <typename T2, class MYCLASS>
  300.     friend void setLazy(MYCLASS* this_, const typename LLLazy<T2>::Factory& factory);
  301.     /// access to LLLazy::set(Factory)
  302.     void set(const typename LLLazy<T>::Factory& factory)
  303.     {
  304.         mInstance.set(factory);
  305.     }
  306.     /// access to LLLazy::set(T*)
  307.     void set(T* instance)
  308.     {
  309.         mInstance.set(instance);
  310.     }
  311. private:
  312.     LLLazy<T> mInstance;
  313. };
  314. /**
  315.  * @name getLazy()/setLazy()
  316.  * Suppose you have something like the following:
  317.  * @code
  318.  * class Outer: public LLLazyBase<DifficultClass>, public LLLazyBase<AwkwardType>
  319.  * {
  320.  *     ...
  321.  * };
  322.  * @endcode
  323.  *
  324.  * Your methods can reference the @c DifficultClass instance using
  325.  * <tt>LLLazyBase<DifficultClass>::get()</tt>, which is admittedly a bit ugly.
  326.  * Alternatively, you can write <tt>getLazy<DifficultClass>(this)</tt>, which
  327.  * is somewhat more straightforward to read.
  328.  *
  329.  * Similarly,
  330.  * @code
  331.  * LLLazyBase<DifficultClass>::set(new TestDifficultClass());
  332.  * @endcode
  333.  * could instead be written:
  334.  * @code
  335.  * setLazy<DifficultClass>(this, new TestDifficultClass());
  336.  * @endcode
  337.  *
  338.  * @note
  339.  * I wanted to provide getLazy() and setLazy() without explicitly passing @c
  340.  * this. That would imply making them methods on a base class rather than free
  341.  * functions. But if <tt>LLLazyBase<T></tt> derives normally from (say) @c
  342.  * LLLazyGrandBase providing those methods, then unqualified getLazy() would
  343.  * be ambiguous: you'd have to write <tt>LLLazyBase<T>::getLazy<T>()</tt>,
  344.  * which is even uglier than <tt>LLLazyBase<T>::get()</tt>, and therefore
  345.  * pointless. You can make the compiler not care which @c LLLazyGrandBase
  346.  * instance you're talking about by making @c LLLazyGrandBase a @c virtual
  347.  * base class of @c LLLazyBase. But in that case,
  348.  * <tt>LLLazyGrandBase::getLazy<T>()</tt> can't access
  349.  * <tt>LLLazyBase<T>::get()</tt>!
  350.  *
  351.  * We want <tt>getLazy<T>()</tt> to access <tt>LLLazyBase<T>::get()</tt> as if
  352.  * in the lexical context of some subclass method. Ironically, free functions
  353.  * let us do that better than methods on a @c virtual base class -- but that
  354.  * implies passing @c this explicitly. So be it.
  355.  */
  356. //@{
  357. #if (! GCC33)
  358. template <typename T, class MYCLASS>
  359. T& getLazy(MYCLASS* this_) { return this_->LLLazyBase<T>::get(); }
  360. template <typename T, class MYCLASS>
  361. const T& getLazy(const MYCLASS* this_) { return this_->LLLazyBase<T>::get(); }
  362. #else // gcc 3.3
  363. // For const-correctness, we really should have two getLazy() variants: one
  364. // accepting const MYCLASS* and returning const T&, the other accepting
  365. // non-const MYCLASS* and returning non-const T&. This works fine on the Mac
  366. // (gcc 4.0.1) and Windows (MSVC 8.0), but fails on our Linux 32-bit Debian
  367. // Sarge stations (gcc 3.3.5). Since I really don't know how to beat that aging
  368. // compiler over the head to make it do the right thing, I'm going to have to
  369. // move forward with the wrong thing: a single getLazy() function that accepts
  370. // const MYCLASS* and returns non-const T&.
  371. template <typename T, class MYCLASS>
  372. T& getLazy(const MYCLASS* this_) { return const_cast<MYCLASS*>(this_)->LLLazyBase<T>::get(); }
  373. #endif // gcc 3.3
  374. template <typename T, class MYCLASS>
  375. void setLazy(MYCLASS* this_, T* instance) { this_->LLLazyBase<T>::set(instance); }
  376. template <typename T, class MYCLASS>
  377. void setLazy(MYCLASS* this_, const typename LLLazy<T>::Factory& factory)
  378. {
  379.     this_->LLLazyBase<T>::set(factory);
  380. }
  381. //@}
  382. #endif /* ! defined(LL_LLLAZY_H) */