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: 22 SkTLazy() = default; SkTLazy(const T * src)23 explicit SkTLazy(const T* src) : fPtr(src ? new (&fStorage) T(*src) : nullptr) {} SkTLazy(const SkTLazy & that)24 SkTLazy(const SkTLazy& that) { *this = that; } SkTLazy(SkTLazy && that)25 SkTLazy(SkTLazy&& that) { *this = std::move(that); } 26 ~SkTLazy()27 ~SkTLazy() { this->reset(); } 28 29 SkTLazy& operator=(const SkTLazy& that) { 30 if (that.isValid()) { 31 this->set(*that); 32 } else { 33 this->reset(); 34 } 35 return *this; 36 } 37 38 SkTLazy& operator=(SkTLazy&& that) { 39 if (that.isValid()) { 40 this->set(std::move(*that)); 41 } else { 42 this->reset(); 43 } 44 return *this; 45 } 46 47 /** 48 * Return a pointer to an instance of the class initialized with 'args'. 49 * If a previous instance had been initialized (either from init() or 50 * set()) it will first be destroyed, so that a freshly initialized 51 * instance is always returned. 52 */ init(Args &&...args)53 template <typename... Args> T* init(Args&&... args) { 54 this->reset(); 55 fPtr = new (&fStorage) T(std::forward<Args>(args)...); 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 */ set(const T & src)65 T* set(const T& src) { 66 if (this->isValid()) { 67 *fPtr = src; 68 } else { 69 fPtr = new (&fStorage) T(src); 70 } 71 return fPtr; 72 } 73 set(T && src)74 T* set(T&& src) { 75 if (this->isValid()) { 76 *fPtr = std::move(src); 77 } else { 78 fPtr = new (&fStorage) T(std::move(src)); 79 } 80 return fPtr; 81 } 82 83 /** 84 * Destroy the lazy object (if it was created via init() or set()) 85 */ reset()86 void reset() { 87 if (this->isValid()) { 88 fPtr->~T(); 89 fPtr = nullptr; 90 } 91 } 92 93 /** 94 * Returns true if a valid object has been initialized in the SkTLazy, 95 * false otherwise. 96 */ isValid()97 bool isValid() const { return SkToBool(fPtr); } 98 99 /** 100 * Returns the object. This version should only be called when the caller 101 * knows that the object has been initialized. 102 */ get()103 T* get() const { SkASSERT(this->isValid()); return fPtr; } 104 T* operator->() const { return this->get(); } 105 T& operator*() const { return *this->get(); } 106 107 /** 108 * Like above but doesn't assert if object isn't initialized (in which case 109 * nullptr is returned). 110 */ getMaybeNull()111 T* getMaybeNull() const { return fPtr; } 112 113 private: 114 typename std::aligned_storage<sizeof(T), alignof(T)>::type fStorage; 115 T* fPtr{nullptr}; // nullptr or fStorage 116 }; 117 118 /** 119 * A helper built on top of SkTLazy to do copy-on-first-write. The object is initialized 120 * with a const pointer but provides a non-const pointer accessor. The first time the 121 * accessor is called (if ever) the object is cloned. 122 * 123 * In the following example at most one copy of constThing is made: 124 * 125 * SkTCopyOnFirstWrite<Thing> thing(&constThing); 126 * ... 127 * function_that_takes_a_const_thing_ptr(thing); // constThing is passed 128 * ... 129 * if (need_to_modify_thing()) { 130 * thing.writable()->modifyMe(); // makes a copy of constThing 131 * } 132 * ... 133 * x = thing->readSomething(); 134 * ... 135 * if (need_to_modify_thing_now()) { 136 * thing.writable()->changeMe(); // makes a copy of constThing if we didn't call modifyMe() 137 * } 138 * 139 * consume_a_thing(thing); // could be constThing or a modified copy. 140 */ 141 template <typename T> 142 class SkTCopyOnFirstWrite { 143 public: SkTCopyOnFirstWrite(const T & initial)144 explicit SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {} 145 SkTCopyOnFirstWrite(const T * initial)146 explicit SkTCopyOnFirstWrite(const T* initial) : fObj(initial) {} 147 148 // Constructor for delayed initialization. SkTCopyOnFirstWrite()149 SkTCopyOnFirstWrite() : fObj(nullptr) {} 150 SkTCopyOnFirstWrite(const SkTCopyOnFirstWrite & that)151 SkTCopyOnFirstWrite(const SkTCopyOnFirstWrite& that) { *this = that; } SkTCopyOnFirstWrite(SkTCopyOnFirstWrite && that)152 SkTCopyOnFirstWrite( SkTCopyOnFirstWrite&& that) { *this = std::move(that); } 153 154 SkTCopyOnFirstWrite& operator=(const SkTCopyOnFirstWrite& that) { 155 fLazy = that.fLazy; 156 fObj = fLazy.isValid() ? fLazy.get() : that.fObj; 157 return *this; 158 } 159 160 SkTCopyOnFirstWrite& operator=(SkTCopyOnFirstWrite&& that) { 161 fLazy = std::move(that.fLazy); 162 fObj = fLazy.isValid() ? fLazy.get() : that.fObj; 163 return *this; 164 } 165 166 // Should only be called once, and only if the default constructor was used. init(const T & initial)167 void init(const T& initial) { 168 SkASSERT(nullptr == fObj); 169 SkASSERT(!fLazy.isValid()); 170 fObj = &initial; 171 } 172 173 /** 174 * Returns a writable T*. The first time this is called the initial object is cloned. 175 */ writable()176 T* writable() { 177 SkASSERT(fObj); 178 if (!fLazy.isValid()) { 179 fLazy.set(*fObj); 180 fObj = fLazy.get(); 181 } 182 return const_cast<T*>(fObj); 183 } 184 get()185 const T* get() const { return fObj; } 186 187 /** 188 * Operators for treating this as though it were a const pointer. 189 */ 190 191 const T *operator->() const { return fObj; } 192 193 operator const T*() const { return fObj; } 194 195 const T& operator *() const { return *fObj; } 196 197 private: 198 const T* fObj; 199 SkTLazy<T> fLazy; 200 }; 201 202 #endif 203