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