PageRenderTime 34ms CodeModel.GetById 18ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llcommon/lllazy.h

https://bitbucket.org/lindenlab/viewer-beta/
C++ Header | 399 lines | 117 code | 23 blank | 259 comment | 6 complexity | b84293b60afef4f9c4270e6366b52e4b MD5 | raw file
  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=viewerlgpl$
 30 * Second Life Viewer Source Code
 31 * Copyright (C) 2010, Linden Research, Inc.
 32 * 
 33 * This library is free software; you can redistribute it and/or
 34 * modify it under the terms of the GNU Lesser General Public
 35 * License as published by the Free Software Foundation;
 36 * version 2.1 of the License only.
 37 * 
 38 * This library is distributed in the hope that it will be useful,
 39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 41 * Lesser General Public License for more details.
 42 * 
 43 * You should have received a copy of the GNU Lesser General Public
 44 * License along with this library; if not, write to the Free Software
 45 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 46 * 
 47 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 48 * $/LicenseInfo$
 49 */
 50
 51#if ! defined(LL_LLLAZY_H)
 52#define LL_LLLAZY_H
 53
 54#include <boost/function.hpp>
 55#include <boost/scoped_ptr.hpp>
 56#include <boost/lambda/construct.hpp>
 57#include <stdexcept>
 58
 59/// LLLazyCommon simply factors out of LLLazy<T> things that don't depend on
 60/// its template parameter.
 61class LLLazyCommon
 62{
 63public:
 64    /**
 65     * This exception is thrown if you try to replace an LLLazy<T>'s factory
 66     * (or T* instance) after it already has an instance in hand. Since T
 67     * might well be stateful, we can't know the effect of silently discarding
 68     * and replacing an existing instance, so we disallow it. This facility is
 69     * intended for testing, and in a test scenario we can definitely control
 70     * that.
 71     */
 72    struct InstanceChange: public std::runtime_error
 73    {
 74        InstanceChange(const std::string& what): std::runtime_error(what) {}
 75    };
 76
 77protected:
 78    /**
 79     * InstanceChange might be appropriate in a couple of different LLLazy<T>
 80     * methods. Factor out the common logic.
 81     */
 82    template <typename PTR>
 83    static void ensureNoInstance(const PTR& ptr)
 84    {
 85        if (ptr)
 86        {
 87            // Too late: we've already instantiated the lazy object. We don't
 88            // know whether it's stateful or not, so it's not safe to discard
 89            // the existing instance in favor of a replacement.
 90            throw InstanceChange("Too late to replace LLLazy instance");
 91        }
 92    }
 93};
 94
 95/**
 96 * LLLazy<T> is useful when you have an outer class Outer that you're trying
 97 * to bring under unit test, that contains a data member difficult to
 98 * instantiate in a test harness. Typically the data member's class Inner has
 99 * many thorny dependencies. Feathers generally advocates "Extract and
100 * Override Factory Method" (p. 350). But in C++, you can't call a derived
101 * class override of a virtual method from the derived class constructor,
102 * which limits applicability of "Extract and Override Factory Method." For
103 * such cases Feathers presents "Extract and Override Getter" (p. 352).
104 *
105 * So we'll assume that your class Outer contains a member like this:
106 * @code
107 * Inner mInner;
108 * @endcode
109 *
110 * LLLazy<Inner> can be used to replace this member. You can directly declare:
111 * @code
112 * LLLazy<Inner> mInner;
113 * @endcode
114 * and change references to mInner accordingly.
115 *
116 * (Alternatively, you can add a base class of the form
117 * <tt>LLLazyBase<Inner></tt>. This is discussed further in the LLLazyBase<T>
118 * documentation.)
119 *
120 * LLLazy<T> binds a <tt>boost::scoped_ptr<T></tt> and a factory functor
121 * returning T*. You can either bind that functor explicitly or let it default
122 * to the expression <tt>new T()</tt>.
123 *
124 * As long as LLLazy<T> remains unreferenced, its T remains uninstantiated.
125 * The first time you use get(), <tt>operator*()</tt> or <tt>operator->()</tt>
126 * it will instantiate its T and thereafter behave like a pointer to it.
127 *
128 * Thus, any existing reference to <tt>mInner.member</tt> should be replaced
129 * with <tt>mInner->member</tt>. Any simple reference to @c mInner should be
130 * replaced by <tt>*mInner</tt>.
131 *
132 * (If the original declaration was a pointer initialized in Outer's
133 * constructor, e.g. <tt>Inner* mInner</tt>, so much the better. In that case
134 * you should be able to drop in <tt>LLLazy<Inner></tt> without much change.)
135 *
136 * The support for "Extract and Override Getter" lies in the fact that you can
137 * replace the factory functor -- or provide an explicit T*. Presumably this
138 * is most useful from a test subclass -- which suggests that your @c mInner
139 * member should be @c protected.
140 *
141 * Note that <tt>boost::lambda::new_ptr<T>()</tt> makes a dandy factory
142 * functor, for either the set() method or LLLazy<T>'s constructor. If your T
143 * requires constructor arguments, use an expression more like
144 * <tt>boost::lambda::bind(boost::lambda::new_ptr<T>(), arg1, arg2, ...)</tt>.
145 *
146 * Of course the point of replacing the functor is to substitute a class that,
147 * though referenced as Inner*, is not an Inner; presumably this is a testing
148 * subclass of Inner (e.g. TestInner). Thus your test subclass TestOuter for
149 * the containing class Outer will contain something like this:
150 * @code
151 * class TestOuter: public Outer
152 * {
153 * public:
154 *     TestOuter()
155 *     {
156 *         // mInner must be 'protected' rather than 'private'
157 *         mInner.set(boost::lambda::new_ptr<TestInner>());
158 *     }
159 *     ...
160 * };
161 * @endcode
162 */
163template <typename T>
164class LLLazy: public LLLazyCommon
165{
166public:
167    /// Any nullary functor returning T* will work as a Factory
168    typedef boost::function<T* ()> Factory;
169
170    /// The default LLLazy constructor uses <tt>new T()</tt> as its Factory
171    LLLazy():
172        mFactory(boost::lambda::new_ptr<T>())
173    {}
174
175    /// Bind an explicit Factory functor
176    LLLazy(const Factory& factory):
177        mFactory(factory)
178    {}
179
180    /// Reference T, instantiating it if this is the first access
181    const T& get() const
182    {
183        if (! mInstance)
184        {
185            // use the bound Factory functor
186            mInstance.reset(mFactory());
187        }
188        return *mInstance;
189    }
190
191    /// non-const get()
192    T& get()
193    {
194        return const_cast<T&>(const_cast<const LLLazy<T>*>(this)->get());
195    }
196
197    /// operator*() is equivalent to get()
198    const T& operator*() const { return get(); }
199    /// operator*() is equivalent to get()
200    T& operator*() { return get(); }
201
202    /**
203     * operator->() must return (something resembling) T*. It's tempting to
204     * return the underlying boost::scoped_ptr<T>, but that would require
205     * breaking out the lazy-instantiation logic from get() into a common
206     * private method. Assume the pointer used for operator->() access is very
207     * short-lived.
208     */
209    const T* operator->() const { return &get(); }
210    /// non-const operator->()
211    T* operator->() { return &get(); }
212
213    /// set(Factory). This will throw InstanceChange if mInstance has already
214    /// been set.
215    void set(const Factory& factory)
216    {
217        ensureNoInstance(mInstance);
218        mFactory = factory;
219    }
220
221    /// set(T*). This will throw InstanceChange if mInstance has already been
222    /// set.
223    void set(T* instance)
224    {
225        ensureNoInstance(mInstance);
226        mInstance.reset(instance);
227    }
228
229private:
230    Factory mFactory;
231    // Consider an LLLazy<T> member of a class we're accessing by const
232    // reference. We want to allow even const methods to touch the LLLazy<T>
233    // member. Hence the actual pointer must be mutable because such access
234    // might assign it.
235    mutable boost::scoped_ptr<T> mInstance;
236};
237
238#if (! defined(__GNUC__)) || (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
239// Not gcc at all, or a gcc more recent than gcc 3.3
240#define GCC33 0
241#else
242#define GCC33 1
243#endif
244
245/**
246 * LLLazyBase<T> wraps LLLazy<T>, giving you an alternative way to replace
247 * <tt>Inner mInner;</tt>. Instead of coding <tt>LLLazy<Inner> mInner</tt>,
248 * you can add LLLazyBase<Inner> to your Outer class's bases, e.g.:
249 * @code
250 * class Outer: public LLLazyBase<Inner>
251 * {
252 *     ...
253 * };
254 * @endcode
255 *
256 * This gives you @c public get() and @c protected set() methods without
257 * having to make your LLLazy<Inner> member @c protected. The tradeoff is that
258 * you must access the wrapped LLLazy<Inner> using get() and set() rather than
259 * with <tt>operator*()</tt> or <tt>operator->()</tt>.
260 *
261 * This mechanism can be used for more than one member, but only if they're of
262 * different types. That is, you can replace:
263 * @code
264 * DifficultClass mDifficult;
265 * AwkwardType    mAwkward;
266 * @endcode
267 * with:
268 * @code
269 * class Outer: public LLLazyBase<DifficultClass>, public LLLazyBase<AwkwardType>
270 * {
271 *     ...
272 * };
273 * @endcode
274 * but for a situation like this:
275 * @code
276 * DifficultClass mMainDifficult, mAuxDifficult;
277 * @endcode
278 * you should directly embed LLLazy<DifficultClass> (q.v.).
279 *
280 * For multiple LLLazyBase bases, e.g. the <tt>LLLazyBase<DifficultClass>,
281 * LLLazyBase<AwkwardType></tt> example above, access the relevant get()/set()
282 * as (e.g.) <tt>LLLazyBase<DifficultClass>::get()</tt>. (This is why you
283 * can't have multiple LLLazyBase<T> of the same T.) For a bit of syntactic
284 * sugar, please see getLazy()/setLazy().
285 */
286template <typename T>
287class LLLazyBase
288{
289public:
290    /// invoke default LLLazy constructor
291    LLLazyBase() {}
292    /// make wrapped LLLazy bind an explicit Factory
293    LLLazyBase(const typename LLLazy<T>::Factory& factory):
294        mInstance(factory)
295    {}
296
297    /// access to LLLazy::get()
298    T& get() { return *mInstance; }
299    /// access to LLLazy::get()
300    const T& get() const { return *mInstance; }
301
302protected:
303    // see getLazy()/setLazy()
304    #if (! GCC33)
305    template <typename T2, class MYCLASS> friend T2& getLazy(MYCLASS* this_);
306    template <typename T2, class MYCLASS> friend const T2& getLazy(const MYCLASS* this_);
307    #else // gcc 3.3
308    template <typename T2, class MYCLASS> friend T2& getLazy(const MYCLASS* this_);
309    #endif // gcc 3.3
310    template <typename T2, class MYCLASS> friend void setLazy(MYCLASS* this_, T2* instance);
311    template <typename T2, class MYCLASS>
312    friend void setLazy(MYCLASS* this_, const typename LLLazy<T2>::Factory& factory);
313
314    /// access to LLLazy::set(Factory)
315    void set(const typename LLLazy<T>::Factory& factory)
316    {
317        mInstance.set(factory);
318    }
319
320    /// access to LLLazy::set(T*)
321    void set(T* instance)
322    {
323        mInstance.set(instance);
324    }
325
326private:
327    LLLazy<T> mInstance;
328};
329
330/**
331 * @name getLazy()/setLazy()
332 * Suppose you have something like the following:
333 * @code
334 * class Outer: public LLLazyBase<DifficultClass>, public LLLazyBase<AwkwardType>
335 * {
336 *     ...
337 * };
338 * @endcode
339 *
340 * Your methods can reference the @c DifficultClass instance using
341 * <tt>LLLazyBase<DifficultClass>::get()</tt>, which is admittedly a bit ugly.
342 * Alternatively, you can write <tt>getLazy<DifficultClass>(this)</tt>, which
343 * is somewhat more straightforward to read.
344 *
345 * Similarly,
346 * @code
347 * LLLazyBase<DifficultClass>::set(new TestDifficultClass());
348 * @endcode
349 * could instead be written:
350 * @code
351 * setLazy<DifficultClass>(this, new TestDifficultClass());
352 * @endcode
353 *
354 * @note
355 * I wanted to provide getLazy() and setLazy() without explicitly passing @c
356 * this. That would imply making them methods on a base class rather than free
357 * functions. But if <tt>LLLazyBase<T></tt> derives normally from (say) @c
358 * LLLazyGrandBase providing those methods, then unqualified getLazy() would
359 * be ambiguous: you'd have to write <tt>LLLazyBase<T>::getLazy<T>()</tt>,
360 * which is even uglier than <tt>LLLazyBase<T>::get()</tt>, and therefore
361 * pointless. You can make the compiler not care which @c LLLazyGrandBase
362 * instance you're talking about by making @c LLLazyGrandBase a @c virtual
363 * base class of @c LLLazyBase. But in that case,
364 * <tt>LLLazyGrandBase::getLazy<T>()</tt> can't access
365 * <tt>LLLazyBase<T>::get()</tt>!
366 *
367 * We want <tt>getLazy<T>()</tt> to access <tt>LLLazyBase<T>::get()</tt> as if
368 * in the lexical context of some subclass method. Ironically, free functions
369 * let us do that better than methods on a @c virtual base class -- but that
370 * implies passing @c this explicitly. So be it.
371 */
372//@{
373#if (! GCC33)
374template <typename T, class MYCLASS>
375T& getLazy(MYCLASS* this_) { return this_->LLLazyBase<T>::get(); }
376template <typename T, class MYCLASS>
377const T& getLazy(const MYCLASS* this_) { return this_->LLLazyBase<T>::get(); }
378#else // gcc 3.3
379// For const-correctness, we really should have two getLazy() variants: one
380// accepting const MYCLASS* and returning const T&, the other accepting
381// non-const MYCLASS* and returning non-const T&. This works fine on the Mac
382// (gcc 4.0.1) and Windows (MSVC 8.0), but fails on our Linux 32-bit Debian
383// Sarge stations (gcc 3.3.5). Since I really don't know how to beat that aging
384// compiler over the head to make it do the right thing, I'm going to have to
385// move forward with the wrong thing: a single getLazy() function that accepts
386// const MYCLASS* and returns non-const T&.
387template <typename T, class MYCLASS>
388T& getLazy(const MYCLASS* this_) { return const_cast<MYCLASS*>(this_)->LLLazyBase<T>::get(); }
389#endif // gcc 3.3
390template <typename T, class MYCLASS>
391void setLazy(MYCLASS* this_, T* instance) { this_->LLLazyBase<T>::set(instance); }
392template <typename T, class MYCLASS>
393void setLazy(MYCLASS* this_, const typename LLLazy<T>::Factory& factory)
394{
395    this_->LLLazyBase<T>::set(factory);
396}
397//@}
398
399#endif /* ! defined(LL_LLLAZY_H) */