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 * Returns true if a valid object has been initialized in the SkTLazy, 79 * false otherwise. 80 */ 81 bool isValid() const { return NULL != fPtr; } 82 83 /** 84 * Returns the object. This version should only be called when the caller 85 * knows that the object has been initialized. 86 */ 87 T* get() const { SkASSERT(this->isValid()); return fPtr; } 88 89 /** 90 * Like above but doesn't assert if object isn't initialized (in which case 91 * NULL is returned). 92 */ 93 T* getMaybeNull() const { return fPtr; } 94 95 private: 96 friend void* operator new<T>(size_t, SkTLazy* lazy); 97 98 T* fPtr; // NULL or fStorage 99 char fStorage[sizeof(T)]; 100 }; 101 102 // Use the below macro (SkNEW_IN_TLAZY) rather than calling this directly 103 template <typename T> void* operator new(size_t, SkTLazy<T>* lazy) { 104 SkASSERT(!lazy->isValid()); 105 lazy->fPtr = reinterpret_cast<T*>(lazy->fStorage); 106 return lazy->fPtr; 107 } 108 109 // Skia doesn't use C++ exceptions but it may be compiled with them enabled. Having an op delete 110 // to match the op new silences warnings about missing op delete when a constructor throws an 111 // exception. 112 template <typename T> void operator delete(void*, SkTLazy<T>*) { SK_CRASH(); } 113 114 // Use this to construct a T inside an SkTLazy using a non-default constructor. 115 #define SkNEW_IN_TLAZY(tlazy_ptr, type_name, args) (new (tlazy_ptr) type_name args) 116 117 /** 118 * A helper built on top of SkTLazy to do copy-on-first-write. The object is initialized 119 * with a const pointer but provides a non-const pointer accessor. The first time the 120 * accessor is called (if ever) the object is cloned. 121 * 122 * In the following example at most one copy of constThing is made: 123 * 124 * SkTCopyOnFirstWrite<Thing> thing(&constThing); 125 * ... 126 * function_that_takes_a_const_thing_ptr(thing); // constThing is passed 127 * ... 128 * if (need_to_modify_thing()) { 129 * thing.writable()->modifyMe(); // makes a copy of constThing 130 * } 131 * ... 132 * x = thing->readSomething(); 133 * ... 134 * if (need_to_modify_thing_now()) { 135 * thing.writable()->changeMe(); // makes a copy of constThing if we didn't call modifyMe() 136 * } 137 * 138 * consume_a_thing(thing); // could be constThing or a modified copy. 139 */ 140 template <typename T> 141 class SkTCopyOnFirstWrite { 142 public: 143 SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {} 144 145 // Constructor for delayed initialization. 146 SkTCopyOnFirstWrite() : fObj(NULL) {} 147 148 // Should only be called once, and only if the default constructor was used. 149 void init(const T& initial) { 150 SkASSERT(NULL == fObj); 151 SkASSERT(!fLazy.isValid()); 152 fObj = &initial; 153 } 154 155 /** 156 * Returns a writable T*. The first time this is called the initial object is cloned. 157 */ 158 T* writable() { 159 SkASSERT(NULL != fObj); 160 if (!fLazy.isValid()) { 161 fLazy.set(*fObj); 162 fObj = fLazy.get(); 163 } 164 return const_cast<T*>(fObj); 165 } 166 167 /** 168 * Operators for treating this as though it were a const pointer. 169 */ 170 171 const T *operator->() const { return fObj; } 172 173 operator const T*() const { return fObj; } 174 175 const T& operator *() const { return *fObj; } 176 177 private: 178 const T* fObj; 179 SkTLazy<T> fLazy; 180 }; 181 182 #endif 183