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