1 2 /* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10 11 #ifndef SkTLazy_DEFINED 12 #define SkTLazy_DEFINED 13 14 #include "SkTypes.h" 15 #include <new> 16 17 /** 18 * Efficient way to defer allocating/initializing a class until it is needed 19 * (if ever). 20 */ 21 template <typename T> class SkTLazy { 22 public: 23 SkTLazy() : fPtr(NULL) {} 24 25 explicit SkTLazy(const T* src) : fPtr(NULL) { 26 if (src) { 27 fPtr = new (fStorage) T(*src); 28 } 29 } 30 31 SkTLazy(const SkTLazy<T>& src) : fPtr(NULL) { 32 if (src.isValid()) { 33 fPtr = new (fStorage) T(*src->get()); 34 } else { 35 fPtr = NULL; 36 } 37 } 38 39 ~SkTLazy() { 40 if (this->isValid()) { 41 fPtr->~T(); 42 } 43 } 44 45 /** 46 * Return a pointer to a default-initialized instance of the class. If a 47 * previous instance had been initialzied (either from init() or set()) it 48 * will first be destroyed, so that a freshly initialized instance is 49 * always returned. 50 */ 51 T* init() { 52 if (this->isValid()) { 53 fPtr->~T(); 54 } 55 fPtr = new (SkTCast<T*>(fStorage)) T; 56 return fPtr; 57 } 58 59 /** 60 * Copy src into this, and return a pointer to a copy of it. Note this 61 * will always return the same pointer, so if it is called on a lazy that 62 * has already been initialized, then this will copy over the previous 63 * contents. 64 */ 65 T* set(const T& src) { 66 if (this->isValid()) { 67 *fPtr = src; 68 } else { 69 fPtr = new (SkTCast<T*>(fStorage)) T(src); 70 } 71 return fPtr; 72 } 73 74 /** 75 * Returns true if a valid object has been initialized in the SkTLazy, 76 * false otherwise. 77 */ 78 bool isValid() const { return NULL != fPtr; } 79 80 /** 81 * Returns either NULL, or a copy of the object that was passed to 82 * set() or the constructor. 83 */ 84 T* get() const { SkASSERT(this->isValid()); return fPtr; } 85 86 private: 87 T* fPtr; // NULL or fStorage 88 char fStorage[sizeof(T)]; 89 }; 90 91 /** 92 * A helper built on top of SkTLazy to do copy-on-first-write. The object is initialized 93 * with a const pointer but provides a non-const pointer accessor. The first time the 94 * accessor is called (if ever) the object is cloned. 95 * 96 * In the following example at most one copy of constThing is made: 97 * 98 * SkTCopyOnFirstWrite<Thing> thing(&constThing); 99 * ... 100 * function_that_takes_a_const_thing_ptr(thing); // constThing is passed 101 * ... 102 * if (need_to_modify_thing()) { 103 * thing.writable()->modifyMe(); // makes a copy of constThing 104 * } 105 * ... 106 * x = thing->readSomething(); 107 * ... 108 * if (need_to_modify_thing_now()) { 109 * thing.writable()->changeMe(); // makes a copy of constThing if we didn't call modifyMe() 110 * } 111 * 112 * consume_a_thing(thing); // could be constThing or a modified copy. 113 */ 114 template <typename T> 115 class SkTCopyOnFirstWrite { 116 public: 117 SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {} 118 119 // Constructor for delayed initialization. 120 SkTCopyOnFirstWrite() : fObj(NULL) {} 121 122 // Should only be called once, and only if the default constructor was used. 123 void init(const T& initial) { 124 SkASSERT(NULL == fObj); 125 SkASSERT(!fLazy.isValid()); 126 fObj = &initial; 127 } 128 129 /** 130 * Returns a writable T*. The first time this is called the initial object is cloned. 131 */ 132 T* writable() { 133 SkASSERT(NULL != fObj); 134 if (!fLazy.isValid()) { 135 fLazy.set(*fObj); 136 fObj = fLazy.get(); 137 } 138 return const_cast<T*>(fObj); 139 } 140 141 /** 142 * Operators for treating this as though it were a const pointer. 143 */ 144 145 const T *operator->() const { return fObj; } 146 147 operator const T*() const { return fObj; } 148 149 const T& operator *() const { return *fObj; } 150 151 private: 152 const T* fObj; 153 SkTLazy<T> fLazy; 154 }; 155 156 #endif 157