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