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