xref: /aosp_15_r20/external/skia/src/base/SkTLazy.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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