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