• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC.
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 SkTOptional_DEFINED
9 #define SkTOptional_DEFINED
10 
11 #include "include/core/SkTypes.h"
12 
13 #include <utility>
14 
15 namespace skstd {
16 
17 /**
18  * An empty optional is represented with `nullopt`.
19  */
20 struct nullopt_t {
21     struct tag {};
22 
23     // nullopt_t must not be default-constructible.
nullopt_tnullopt_t24     explicit constexpr nullopt_t(tag) {}
25 };
26 
27 static constexpr nullopt_t nullopt{nullopt_t::tag{}};
28 
29 /**
30  * Simple drop-in replacement for std::optional until we move to C++17. This does not have all of
31  * std::optional's capabilities, but it covers our needs for the time being.
32  */
33 template<typename T>
34 class optional {
35 public:
optional(const T & value)36     optional(const T& value)
37         : fHasValue(true) {
38         new(&fPayload.fValue) T(value);
39     }
40 
optional(T && value)41     optional(T&& value)
42         : fHasValue(true) {
43         new(&fPayload.fValue) T(std::move(value));
44     }
45 
optional()46     optional() {}
47 
optional(const optional & other)48     optional(const optional& other) {
49         *this = other;
50     }
51 
52     // Construction with nullopt is the same as default construction.
optional(nullopt_t)53     optional(nullopt_t) : optional() {}
54 
55     // We need a non-const copy constructor because otherwise optional(nonConstSrc) isn't an exact
56     // match for the copy constructor, and we'd end up invoking the Args&&... template by mistake.
optional(optional & other)57     optional(optional& other) {
58         *this = other;
59     }
60 
optional(optional && other)61     optional(optional&& other) {
62         *this = std::move(other);
63     }
64 
65     template<typename... Args>
optional(Args &&...args)66     optional(Args&&... args) {
67         fHasValue = true;
68         new(&fPayload.fValue) T(std::forward<Args>(args)...);
69     }
70 
~optional()71     ~optional() {
72         this->reset();
73     }
74 
75     optional& operator=(const optional& other) {
76         if (this != &other) {
77             if (fHasValue) {
78                 if (other.fHasValue) {
79                     fPayload.fValue = other.fPayload.fValue;
80                 } else {
81                     this->reset();
82                 }
83             } else {
84                 if (other.fHasValue) {
85                     fHasValue = true;
86                     new (&fPayload.fValue) T(other.fPayload.fValue);
87                 } else {
88                     // do nothing, no value on either side
89                 }
90             }
91         }
92         return *this;
93     }
94 
95     optional& operator=(optional&& other) {
96         if (this != &other) {
97             if (fHasValue) {
98                 if (other.fHasValue) {
99                     fPayload.fValue = std::move(other.fPayload.fValue);
100                 } else {
101                     this->reset();
102                 }
103             } else {
104                 if (other.fHasValue) {
105                     fHasValue = true;
106                     new (&fPayload.fValue) T(std::move(other.fPayload.fValue));
107                 } else {
108                     // do nothing, no value on either side
109                 }
110             }
111         }
112         return *this;
113     }
114 
115     template<typename... Args>
emplace(Args &&...args)116     optional& emplace(Args&&... args) {
117         this->reset();
118         fHasValue = true;
119         new(&fPayload.fValue) T(std::forward<Args>(args)...);
120         return *this;
121     }
122 
123     template<typename U, typename... Args>
emplace(std::initializer_list<U> il,Args &&...args)124     optional& emplace(std::initializer_list<U> il, Args&&... args) {
125         this->reset();
126         fHasValue = true;
127         new(&fPayload.fValue) T(il, std::forward<Args>(args)...);
128         return *this;
129     }
130 
131     // Assignment to nullopt is the same as reset().
132     optional& operator=(nullopt_t) {
133         this->reset();
134         return *this;
135     }
136 
137     T& operator*() & {
138         SkASSERT(fHasValue);
139         return fPayload.fValue;
140     }
141 
142     const T& operator*() const& {
143         SkASSERT(fHasValue);
144         return fPayload.fValue;
145     }
146 
147     T&& operator*() && {
148         SkASSERT(fHasValue);
149         return std::move(fPayload.fValue);
150     }
151 
152     const T&& operator*() const&& {
153         SkASSERT(fHasValue);
154         return std::move(fPayload.fValue);
155     }
156 
value()157     const T& value() const& {
158         SkASSERT_RELEASE(fHasValue);
159         return **this;
160     }
161 
value()162     T& value() & {
163         SkASSERT_RELEASE(fHasValue);
164         return **this;
165     }
166 
value()167     const T&& value() const&& {
168         SkASSERT_RELEASE(fHasValue);
169         return std::move(**this);
170     }
171 
value()172     T&& value() && {
173         SkASSERT_RELEASE(fHasValue);
174         return std::move(**this);
175     }
176 
177     T* operator->() {
178         return &**this;
179     }
180 
181     const T* operator->() const {
182         return &**this;
183     }
184 
185     template<typename U>
value_or(U && value)186     T value_or(U&& value) const& {
187         return this->has_value() ? **this : static_cast<T>(std::forward<U>(value));
188     }
189 
190     template<typename U>
value_or(U && value)191     T value_or(U&& value) && {
192         return this->has_value() ? std::move(**this) : static_cast<T>(std::forward<U>(value));
193     }
194 
has_value()195     bool has_value() const {
196         return fHasValue;
197     }
198 
199     explicit operator bool() const {
200         return this->has_value();
201     }
202 
reset()203     void reset() {
204         if (fHasValue) {
205             fPayload.fValue.~T();
206             fHasValue = false;
207         }
208     }
209 
210 private:
211     union Payload {
212         T fValue;
213 
Payload()214         Payload() {}
215 
~Payload()216         ~Payload() {}
217     } fPayload;
218 
219     bool fHasValue = false;
220 };
221 
222 // Comparison operators for optional x optional
223 template <typename T, typename U> bool operator==(const optional<T>& a, const optional<U>& b) {
224     return (a.has_value() != b.has_value()) ? false :
225                             !a.has_value()  ? true :
226                                               (*a == *b);
227 }
228 
229 template <typename T, typename U> bool operator!=(const optional<T>& a, const optional<U>& b) {
230     return (a.has_value() != b.has_value()) ? true :
231                             !a.has_value()  ? false :
232                                               (*a != *b);
233 }
234 
235 template <typename T, typename U> bool operator<(const optional<T>& a, const optional<U>& b) {
236     return !b.has_value() ? false :
237            !a.has_value() ? true :
238                             (*a < *b);
239 }
240 
241 template <typename T, typename U> bool operator<=(const optional<T>& a, const optional<U>& b) {
242     return !a.has_value() ? true :
243            !b.has_value() ? false :
244                             (*a <= *b);
245 }
246 
247 template <typename T, typename U> bool operator>(const optional<T>& a, const optional<U>& b) {
248     return !a.has_value() ? false :
249            !b.has_value() ? true :
250                             (*a > *b);
251 }
252 
253 template <typename T, typename U> bool operator>=(const optional<T>& a, const optional<U>& b) {
254     return !b.has_value() ? true :
255            !a.has_value() ? false :
256                             (*a >= *b);
257 }
258 
259 // Comparison operators for optional x nullopt
260 template <typename T> bool operator==(const optional<T>& a, nullopt_t) {
261     return !a.has_value();
262 }
263 
264 template <typename T> bool operator!=(const optional<T>& a, nullopt_t) {
265     return a.has_value();
266 }
267 
268 template <typename T> bool operator<(const optional<T>&, nullopt_t) {
269     return false;
270 }
271 
272 template <typename T> bool operator<=(const optional<T>& a, nullopt_t) {
273     return !a.has_value();
274 }
275 
276 template <typename T> bool operator>(const optional<T>& a, nullopt_t) {
277     return a.has_value();
278 }
279 
280 template <typename T>
281 bool operator>=(const optional<T>&, nullopt_t) {
282     return true;
283 }
284 
285 // Comparison operators for nullopt x optional
286 template <typename U> bool operator==(nullopt_t, const optional<U>& b) {
287     return !b.has_value();
288 }
289 
290 template <typename U> bool operator!=(nullopt_t, const optional<U>& b) {
291     return b.has_value();
292 }
293 
294 template <typename U> bool operator<(nullopt_t, const optional<U>& b) {
295   return b.has_value();
296 }
297 
298 template <typename U> bool operator<=(nullopt_t, const optional<U>&) {
299     return true;
300 }
301 
302 template <typename U> bool operator>(nullopt_t, const optional<U>&) {
303     return false;
304 }
305 
306 template <typename U> bool operator>=(nullopt_t, const optional<U>& b) {
307     return !b.has_value();
308 }
309 
310 // Comparison operators for optional x value
311 template <typename T, typename U> bool operator==(const optional<T>& a, const U& b) {
312     return a.has_value() && (*a == b);
313 }
314 
315 template <typename T, typename U> bool operator!=(const optional<T>& a, const U& b) {
316     return !a.has_value() || (*a != b);
317 }
318 
319 template <typename T, typename U> bool operator<(const optional<T>& a, const U& b) {
320     return !a.has_value() || (*a < b);
321 }
322 
323 template <typename T, typename U> bool operator<=(const optional<T>& a, const U& b) {
324     return !a.has_value() || (*a <= b);
325 }
326 
327 template <typename T, typename U> bool operator>(const optional<T>& a, const U& b) {
328   return a.has_value() && (*a > b);
329 }
330 
331 template <typename T, typename U> bool operator>=(const optional<T>& a, const U& b) {
332   return a.has_value() && (*a >= b);
333 }
334 
335 // Comparison operators for value x optional
336 template <typename T, typename U> bool operator==(const T& a, const optional<U>& b) {
337     return b.has_value() && (a == *b);
338 }
339 
340 template <typename T, typename U> bool operator!=(const T& a, const optional<U>& b) {
341     return !b.has_value() || (a != *b);
342 }
343 
344 template <typename T, typename U> bool operator<(const T& a, const optional<U>& b) {
345     return b.has_value() && (a < *b);
346 }
347 
348 template <typename T, typename U> bool operator<=(const T& a, const optional<U>& b) {
349     return b.has_value() && (a <= *b);
350 }
351 
352 template <typename T, typename U> bool operator>(const T& a, const optional<U>& b) {
353     return !b.has_value() || (a > *b);
354 }
355 
356 template <typename T, typename U> bool operator>=(const T& a, const optional<U>& b) {
357     return !b.has_value() || (a >= *b);
358 }
359 
360 } // namespace skstd
361 
362 #endif
363