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_tskstd::nullopt_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
operator =(const optional& other)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
operator =(optional&& other)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().
operator =(nullopt_t)132 optional& operator=(nullopt_t) {
133 this->reset();
134 return *this;
135 }
136
operator *()137 T& operator*() & {
138 SkASSERT(fHasValue);
139 return fPayload.fValue;
140 }
141
operator *() const142 const T& operator*() const& {
143 SkASSERT(fHasValue);
144 return fPayload.fValue;
145 }
146
operator *()147 T&& operator*() && {
148 SkASSERT(fHasValue);
149 return std::move(fPayload.fValue);
150 }
151
operator *() const152 const T&& operator*() const&& {
153 SkASSERT(fHasValue);
154 return std::move(fPayload.fValue);
155 }
156
value() const157 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() const167 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
operator ->()177 T* operator->() {
178 return &**this;
179 }
180
operator ->() const181 const T* operator->() const {
182 return &**this;
183 }
184
185 template<typename U>
value_or(U&& value) const186 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() const195 bool has_value() const {
196 return fHasValue;
197 }
198
operator bool() const199 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
operator ==(const optional<T>& a, const optional<U>& b)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
operator !=(const optional<T>& a, const optional<U>& b)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
operator <(const optional<T>& a, const optional<U>& b)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
operator <=(const optional<T>& a, const optional<U>& b)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
operator >(const optional<T>& a, const optional<U>& b)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
operator >=(const optional<T>& a, const optional<U>& b)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
operator ==(const optional<T>& a, nullopt_t)260 template <typename T> bool operator==(const optional<T>& a, nullopt_t) {
261 return !a.has_value();
262 }
263
operator !=(const optional<T>& a, nullopt_t)264 template <typename T> bool operator!=(const optional<T>& a, nullopt_t) {
265 return a.has_value();
266 }
267
operator <(const optional<T>&, nullopt_t)268 template <typename T> bool operator<(const optional<T>&, nullopt_t) {
269 return false;
270 }
271
operator <=(const optional<T>& a, nullopt_t)272 template <typename T> bool operator<=(const optional<T>& a, nullopt_t) {
273 return !a.has_value();
274 }
275
operator >(const optional<T>& a, nullopt_t)276 template <typename T> bool operator>(const optional<T>& a, nullopt_t) {
277 return a.has_value();
278 }
279
280 template <typename T>
operator >=(const optional<T>&, nullopt_t)281 bool operator>=(const optional<T>&, nullopt_t) {
282 return true;
283 }
284
285 // Comparison operators for nullopt x optional
operator ==(nullopt_t, const optional<U>& b)286 template <typename U> bool operator==(nullopt_t, const optional<U>& b) {
287 return !b.has_value();
288 }
289
operator !=(nullopt_t, const optional<U>& b)290 template <typename U> bool operator!=(nullopt_t, const optional<U>& b) {
291 return b.has_value();
292 }
293
operator <(nullopt_t, const optional<U>& b)294 template <typename U> bool operator<(nullopt_t, const optional<U>& b) {
295 return b.has_value();
296 }
297
operator <=(nullopt_t, const optional<U>&)298 template <typename U> bool operator<=(nullopt_t, const optional<U>&) {
299 return true;
300 }
301
operator >(nullopt_t, const optional<U>&)302 template <typename U> bool operator>(nullopt_t, const optional<U>&) {
303 return false;
304 }
305
operator >=(nullopt_t, const optional<U>& b)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
operator ==(const optional<T>& a, const U& b)311 template <typename T, typename U> bool operator==(const optional<T>& a, const U& b) {
312 return a.has_value() && (*a == b);
313 }
314
operator !=(const optional<T>& a, const U& b)315 template <typename T, typename U> bool operator!=(const optional<T>& a, const U& b) {
316 return !a.has_value() || (*a != b);
317 }
318
operator <(const optional<T>& a, const U& b)319 template <typename T, typename U> bool operator<(const optional<T>& a, const U& b) {
320 return !a.has_value() || (*a < b);
321 }
322
operator <=(const optional<T>& a, const U& b)323 template <typename T, typename U> bool operator<=(const optional<T>& a, const U& b) {
324 return !a.has_value() || (*a <= b);
325 }
326
operator >(const optional<T>& a, const U& b)327 template <typename T, typename U> bool operator>(const optional<T>& a, const U& b) {
328 return a.has_value() && (*a > b);
329 }
330
operator >=(const optional<T>& a, const U& b)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
operator ==(const T& a, const optional<U>& b)336 template <typename T, typename U> bool operator==(const T& a, const optional<U>& b) {
337 return b.has_value() && (a == *b);
338 }
339
operator !=(const T& a, const optional<U>& b)340 template <typename T, typename U> bool operator!=(const T& a, const optional<U>& b) {
341 return !b.has_value() || (a != *b);
342 }
343
operator <(const T& a, const optional<U>& b)344 template <typename T, typename U> bool operator<(const T& a, const optional<U>& b) {
345 return b.has_value() && (a < *b);
346 }
347
operator <=(const T& a, const optional<U>& b)348 template <typename T, typename U> bool operator<=(const T& a, const optional<U>& b) {
349 return b.has_value() && (a <= *b);
350 }
351
operator >(const T& a, const optional<U>& b)352 template <typename T, typename U> bool operator>(const T& a, const optional<U>& b) {
353 return !b.has_value() || (a > *b);
354 }
355
operator >=(const T& a, const optional<U>& b)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