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 template <typename T> class SkTLazy; 18 template <typename T> void* operator new(size_t, SkTLazy<T>* lazy); 19 20 /** 21 * Efficient way to defer allocating/initializing a class until it is needed 22 * (if ever). 23 */ 24 template <typename T> class SkTLazy { 25 public: 26 SkTLazy() : fPtr(NULL) {} 27 28 explicit SkTLazy(const T* src) : fPtr(NULL) { 29 if (src) { 30 fPtr = new (fStorage) T(*src); 31 } 32 } 33 34 SkTLazy(const SkTLazy<T>& src) : fPtr(NULL) { 35 if (src.isValid()) { 36 fPtr = new (fStorage) T(*src->get()); 37 } else { 38 fPtr = NULL; 39 } 40 } 41 42 ~SkTLazy() { 43 if (this->isValid()) { 44 fPtr->~T(); 45 } 46 } 47 48 /** 49 * Return a pointer to a default-initialized instance of the class. If a 50 * previous instance had been initialized (either from init() or set()) it 51 * will first be destroyed, so that a freshly initialized instance is 52 * always returned. 53 */ 54 T* init() { 55 if (this->isValid()) { 56 fPtr->~T(); 57 } 58 fPtr = new (SkTCast<T*>(fStorage)) T; 59 return fPtr; 60 } 61 62 /** 63 * Copy src into this, and return a pointer to a copy of it. Note this 64 * will always return the same pointer, so if it is called on a lazy that 65 * has already been initialized, then this will copy over the previous 66 * contents. 67 */ 68 T* set(const T& src) { 69 if (this->isValid()) { 70 *fPtr = src; 71 } else { 72 fPtr = new (SkTCast<T*>(fStorage)) T(src); 73 } 74 return fPtr; 75 } 76 77 /** 78 * Destroy the lazy object (if it was created via init() or set()) 79 */ 80 void reset() { 81 if (this->isValid()) { 82 fPtr->~T(); 83 fPtr = NULL; 84 } 85 } 86 87 /** 88 * Returns true if a valid object has been initialized in the SkTLazy, 89 * false otherwise. 90 */ 91 bool isValid() const { return SkToBool(fPtr); } 92 93 /** 94 * Returns the object. This version should only be called when the caller 95 * knows that the object has been initialized. 96 */ 97 T* get() const { SkASSERT(this->isValid()); return fPtr; } 98 99 /** 100 * Like above but doesn't assert if object isn't initialized (in which case 101 * NULL is returned). 102 */ 103 T* getMaybeNull() const { return fPtr; } 104 105 private: 106 friend void* operator new<T>(size_t, SkTLazy* lazy); 107 108 T* fPtr; // NULL or fStorage 109 char fStorage[sizeof(T)]; 110 }; 111 112 // Use the below macro (SkNEW_IN_TLAZY) rather than calling this directly 113 template <typename T> void* operator new(size_t, SkTLazy<T>* lazy) { 114 SkASSERT(!lazy->isValid()); 115 lazy->fPtr = reinterpret_cast<T*>(lazy->fStorage); 116 return lazy->fPtr; 117 } 118 119 // Skia doesn't use C++ exceptions but it may be compiled with them enabled. Having an op delete 120 // to match the op new silences warnings about missing op delete when a constructor throws an 121 // exception. 122 template <typename T> void operator delete(void*, SkTLazy<T>*) { SK_CRASH(); } 123 124 // Use this to construct a T inside an SkTLazy using a non-default constructor. 125 #define SkNEW_IN_TLAZY(tlazy_ptr, type_name, args) (new (tlazy_ptr) type_name args) 126 127 /** 128 * A helper built on top of SkTLazy to do copy-on-first-write. The object is initialized 129 * with a const pointer but provides a non-const pointer accessor. The first time the 130 * accessor is called (if ever) the object is cloned. 131 * 132 * In the following example at most one copy of constThing is made: 133 * 134 * SkTCopyOnFirstWrite<Thing> thing(&constThing); 135 * ... 136 * function_that_takes_a_const_thing_ptr(thing); // constThing is passed 137 * ... 138 * if (need_to_modify_thing()) { 139 * thing.writable()->modifyMe(); // makes a copy of constThing 140 * } 141 * ... 142 * x = thing->readSomething(); 143 * ... 144 * if (need_to_modify_thing_now()) { 145 * thing.writable()->changeMe(); // makes a copy of constThing if we didn't call modifyMe() 146 * } 147 * 148 * consume_a_thing(thing); // could be constThing or a modified copy. 149 */ 150 template <typename T> 151 class SkTCopyOnFirstWrite { 152 public: 153 SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {} 154 155 // Constructor for delayed initialization. 156 SkTCopyOnFirstWrite() : fObj(NULL) {} 157 158 // Should only be called once, and only if the default constructor was used. 159 void init(const T& initial) { 160 SkASSERT(NULL == fObj); 161 SkASSERT(!fLazy.isValid()); 162 fObj = &initial; 163 } 164 165 /** 166 * Returns a writable T*. The first time this is called the initial object is cloned. 167 */ 168 T* writable() { 169 SkASSERT(fObj); 170 if (!fLazy.isValid()) { 171 fLazy.set(*fObj); 172 fObj = fLazy.get(); 173 } 174 return const_cast<T*>(fObj); 175 } 176 177 /** 178 * Operators for treating this as though it were a const pointer. 179 */ 180 181 const T *operator->() const { return fObj; } 182 183 operator const T*() const { return fObj; } 184 185 const T& operator *() const { return *fObj; } 186 187 private: 188 const T* fObj; 189 SkTLazy<T> fLazy; 190 }; 191 192 #endif 193