1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef ASTC_CODEC_BASE_OPTIONAL_H_
16 #define ASTC_CODEC_BASE_OPTIONAL_H_
17
18 #include "src/base/type_traits.h"
19
20 #include <cassert>
21 #include <initializer_list>
22 #include <type_traits>
23 #include <utility>
24
25 #include <cstddef>
26
27 // Optional<T> - a template class to store an optional value of type T.
28 //
29 // Usage examples:
30 //
31 // Initialization and construction:
32 // Optional<Foo> foo; // |foo| doesn't contain a value.
33 // Optional<Foo> foo(Foo(10)); // |foo| contains a copy-constructed value.
34 // Optional<Foo> foo2(foo); // |foo2| contains a copy of |foo|'s value.
35 // Optional<Foo> foo3(std::move(foo2)); // Guess what?
36 //
37 // Assignment:
38 // Foo foo_value(0);
39 // Optional<Foo> foo; // |foo| is empty.
40 // Optional<Foo> foo2; // |foo2| is empty.
41 // foo2 = foo; // |foo2| is still empty.
42 // foo = foo_value; // set value of |foo| to a copy of |foo_value|
43 // foo = std::move(foo_value); // move |foo_value| into |foo|.
44 // foo2 = foo; // now |foo2| has a copy of |foo|'s value.
45 // foo = kNullopt; // unset |foo|, it has no value.
46 //
47 // Checking and accessing value:
48 // if (foo) {
49 // // |foo| has a value.
50 // doStuff(*foo); // |*foo| is the value inside |foo|.
51 // foo->callMethod(); // Same as (*foo).callMethod().
52 // } else {
53 // // |foo| is empty.
54 // }
55 //
56 // foo.value() // Same as *foo
57 // foo.valueOr(<default>) // Return <default> is |foo| has no value.
58 //
59 // In-place construction:
60 //
61 // Optional<Foo> foo; // |foo| is empty.
62 // foo.emplace(20); // |foo| now contains a value constructed as Foo(20)
63 //
64 // Optional<Foo> foo(kInplace, 20); // |foo| is initialized with a value
65 // // that is constructed in-place as
66 // // Foo(20).
67 //
68 // return makeOptional<Foo>(20); // Takes Foo constructor arguments
69 // // directly.
70 //
71 // Returning values:
72 //
73 // Optional<Foo> myFunc(...) {
74 // if (someCondition) {
75 // return Foo(10); // call Optional<Foo>(Foo&) constructor.
76 // } else {
77 // return {}; // call Optional<Foo>() constructor, which
78 // // builds an empty value.
79 // }
80 // }
81 //
82 // Memory layout:
83 // Optional<Foo> is equivalent to:
84 //
85 // struct {
86 // bool flag;
87 // Foo value;
88 // };
89 //
90 // in terms of memory layout. This means it *doubles* the size of integral
91 // types. Also:
92 //
93 // - Optional<Foo> can be constructed from anything that constructs a Foo.
94 //
95 // - Same with Optional<Foo>(kInplace, Args...) where Args... matches any
96 // arguments that can be passed to a Foo constructor.
97 //
98 // - Comparison operators are provided. Beware: an empty Optional<Foo>
99 // is always smaller than any Foo value.
100
101 namespace astc_codec {
102 namespace base {
103
104 namespace details {
105
106 // Base classes to reduce the number of instantiations of the Optional's
107 // internal members.
108 class OptionalFlagBase {
109 public:
setConstructed(bool constructed)110 void setConstructed(bool constructed) { mConstructed = constructed; }
constructed()111 constexpr bool constructed() const { return mConstructed; }
112 constexpr operator bool() const { return constructed(); }
hasValue()113 bool hasValue() const { return constructed(); }
114
115 constexpr OptionalFlagBase(bool constructed = false)
mConstructed(constructed)116 : mConstructed(constructed) { }
117
118 private:
119 bool mConstructed = false;
120 };
121
122 template<size_t Size, size_t Align>
123 class OptionalStorageBase {
124 protected:
125 using StoreT = typename std::aligned_storage<Size, Align>::type;
126 StoreT mStorage = {};
127 };
128
129 } // namespace details
130
131 // A tag type for empty optional construction
132 struct NulloptT {
NulloptTNulloptT133 constexpr explicit NulloptT(int) { }
134 };
135
136 // A tag type for inplace value construction
137 struct InplaceT {
InplaceTInplaceT138 constexpr explicit InplaceT(int) { }
139 };
140
141 // Tag values for null optional and inplace construction
142 constexpr NulloptT kNullopt{1};
143 constexpr InplaceT kInplace{1};
144
145 // Forward declaration for an early use
146 template<class T>
147 class Optional;
148
149 // A type trait for checking if a type is an optional instantiation
150 // Note: if you want to refer to the template name inside the template,
151 // you need to declare this alias outside of it - because the
152 // class name inside of the template stands for an instantiated template
153 // E.g, for template <T> class Foo if you say 'Foo' inside the class, it
154 // actually means Foo<T>;
155 template<class U>
156 using is_any_optional =
157 is_template_instantiation_of<typename std::decay<U>::type, Optional>;
158
159 template<class T>
160 class Optional
161 : private details::OptionalFlagBase,
162 private details::OptionalStorageBase<sizeof(T),
163 std::alignment_of<T>::value> {
164 // make sure all optionals are buddies - this is needed to implement
165 // conversion from optionals of other types
166 template<class U>
167 friend class Optional;
168
169 template<class U>
170 using self = Optional<U>;
171
172 using base_flag = details::OptionalFlagBase;
173 using base_storage =
174 details::OptionalStorageBase<sizeof(T), std::alignment_of<T>::value>;
175
176 public:
177 // std::optional will have this, so let's provide it
178 using value_type = T;
179
180 // make sure we forbid some Optional instantiations where things may get
181 // really messy
182 static_assert(!std::is_same<typename std::decay<T>::type, NulloptT>::value,
183 "Optional of NulloptT is not allowed");
184 static_assert(!std::is_same<typename std::decay<T>::type, InplaceT>::value,
185 "Optional of InplaceT is not allowed");
186 static_assert(!std::is_reference<T>::value,
187 "Optional references are not allowed: use a pointer instead");
188
189 // constructors
Optional()190 constexpr Optional() { }
Optional(NulloptT)191 constexpr Optional(NulloptT) { }
192
Optional(const Optional & other)193 Optional(const Optional& other) : base_flag(other.constructed()) {
194 if (this->constructed()) {
195 new (&get()) T(other.get());
196 }
197 }
Optional(Optional && other)198 Optional(Optional&& other) : base_flag(other.constructed()) {
199 if (this->constructed()) {
200 new (&get()) T(std::move(other.get()));
201 }
202 }
203
204 // Conversion constructor from optional of similar type
205 template<class U, class = enable_if_c<!is_any_optional<U>::value &&
206 std::is_constructible<T, U>::value>>
Optional(const Optional<U> & other)207 Optional(const Optional<U>& other) : base_flag(other.constructed()) {
208 if (this->constructed()) {
209 new (&get()) T(other.get());
210 }
211 }
212
213 // Move-conversion constructor
214 template<class U, class = enable_if_c<!is_any_optional<U>::value &&
215 std::is_constructible<T, U>::value>>
Optional(Optional<U> && other)216 Optional(Optional<U>&& other) : base_flag(other.constructed()) {
217 if (this->constructed()) {
218 new (&get()) T(std::move(other.get()));
219 }
220 }
221
222 // Construction from a raw value
Optional(const T & value)223 Optional(const T& value) : base_flag(true) { new (&get()) T(value); }
224 // Move construction from a raw value
Optional(T && value)225 Optional(T&& value) : base_flag(true) { new (&get()) T(std::move(value)); }
226
227 // Inplace construction from a list of |T|'s ctor arguments
228 template<class... Args>
Optional(InplaceT,Args &&...args)229 Optional(InplaceT, Args&&... args) : base_flag(true) {
230 new (&get()) T(std::forward<Args>(args)...);
231 }
232
233 // Inplace construction from an initializer list passed into |T|'s ctor
234 template<class U, class = enable_if<
235 std::is_constructible<T, std::initializer_list<U>>>>
Optional(InplaceT,std::initializer_list<U> il)236 Optional(InplaceT, std::initializer_list<U> il) : base_flag(true) {
237 new (&get()) T(il);
238 }
239
240 // direct assignment
241 Optional& operator=(const Optional& other) {
242 if (&other == this) {
243 return *this;
244 }
245
246 if (this->constructed()) {
247 if (other.constructed()) {
248 get() = other.get();
249 } else {
250 destruct();
251 this->setConstructed(false);
252 }
253 } else {
254 if (other.constructed()) {
255 new (&get()) T(other.get());
256 this->setConstructed(true);
257 } else {
258 ; // we're good
259 }
260 }
261 return *this;
262 }
263
264 // move assignment
265 Optional& operator=(Optional&& other) {
266 if (this->constructed()) {
267 if (other.constructed()) {
268 get() = std::move(other.get());
269 } else {
270 destruct();
271 this->setConstructed(false);
272 }
273 } else {
274 if (other.constructed()) {
275 new (&get()) T(std::move(other.get()));
276 this->setConstructed(true);
277 } else {
278 ; // we're good
279 }
280 }
281 return *this;
282 }
283
284 // conversion assignment
285 template<class U,
286 class = enable_if_convertible<typename std::decay<U>::type, T>>
287 Optional& operator=(const Optional<U>& other) {
288 if (this->constructed()) {
289 if (other.constructed()) {
290 get() = other.get();
291 } else {
292 destruct();
293 this->setConstructed(false);
294 }
295 } else {
296 if (other.constructed()) {
297 new (&get()) T(other.get());
298 this->setConstructed(true);
299 } else {
300 ; // we're good
301 }
302 }
303 return *this;
304 }
305
306 // conversion move assignment
307 template<class U,
308 class = enable_if_convertible<typename std::decay<U>::type, T>>
309 Optional& operator=(Optional<U>&& other) {
310 if (this->constructed()) {
311 if (other.constructed()) {
312 get() = std::move(other.get());
313 } else {
314 destruct();
315 this->setConstructed(false);
316 }
317 } else {
318 if (other.constructed()) {
319 new (&get()) T(std::move(other.get()));
320 this->setConstructed(true);
321 } else {
322 ; // we're good
323 }
324 }
325 return *this;
326 }
327
328 // the most complicated one: forwarding constructor for anything convertible
329 // to |T|, excluding the stuff implemented above explicitly
330 template<class U,
331 class = enable_if_c<
332 !is_any_optional<typename std::decay<U>::type>::value &&
333 std::is_convertible<typename std::decay<U>::type, T>::value>>
334 Optional& operator=(U&& other) {
335 if (this->constructed()) {
336 get() = std::forward<U>(other);
337 } else {
338 new (&get()) T(std::forward<U>(other));
339 this->setConstructed(true);
340 }
341 return *this;
342 }
343
344 // Adopt value checkers from the parent
345 using base_flag::operator bool;
346 using base_flag::hasValue;
347
value()348 T& value() {
349 assert(this->constructed());
350 return get();
351 }
value()352 constexpr const T& value() const {
353 assert(this->constructed());
354 return get();
355 }
356
ptr()357 T* ptr() { return this->constructed() ? &get() : nullptr; }
ptr()358 constexpr const T* ptr() const {
359 return this->constructed() ? &get() : nullptr;
360 }
361
362 // Value getter with fallback
363 template<class U = T,
364 class = enable_if_convertible<typename std::decay<U>::type, T>>
valueOr(U && defaultValue)365 constexpr T valueOr(U&& defaultValue) const {
366 return this->constructed() ? get() : std::move(defaultValue);
367 }
368
369 // Pointer-like operators
370 T& operator*() {
371 assert(this->constructed());
372 return get();
373 }
374 constexpr const T& operator*() const {
375 assert(this->constructed());
376 return get();
377 }
378
379 T* operator->() {
380 assert(this->constructed());
381 return &get();
382 }
383 constexpr const T* operator->() const {
384 assert(this->constructed());
385 return &get();
386 }
387
~Optional()388 ~Optional() {
389 if (this->constructed()) {
390 destruct();
391 }
392 }
393
clear()394 void clear() {
395 if (this->constructed()) {
396 destruct();
397 this->setConstructed(false);
398 }
399 }
400
401 template<class U,
402 class = enable_if_convertible<typename std::decay<U>::type, T>>
reset(U && u)403 void reset(U&& u) {
404 *this = std::forward<U>(u);
405 }
406
407 // In-place construction with possible destruction of the old value
408 template<class... Args>
emplace(Args &&...args)409 void emplace(Args&&... args) {
410 if (this->constructed()) {
411 destruct();
412 }
413 new (&get()) T(std::forward<Args>(args)...);
414 this->setConstructed(true);
415 }
416
417 // In-place construction with possible destruction of the old value
418 // initializer-list version
419 template<class U, class = enable_if<
420 std::is_constructible<T, std::initializer_list<U>>>>
emplace(std::initializer_list<U> il)421 void emplace(std::initializer_list<U> il) {
422 if (this->constructed()) {
423 destruct();
424 }
425 new (&get()) T(il);
426 this->setConstructed(true);
427 }
428
429 private:
430 // A helper function to convert the internal raw storage to T&
get()431 constexpr const T& get() const {
432 return *reinterpret_cast<const T*>(
433 reinterpret_cast<const char*>(&this->mStorage));
434 }
435
436 // Same thing, mutable
get()437 T& get() { return const_cast<T&>(const_cast<const Optional*>(this)->get()); }
438
439 // Shortcut for a destructor call for the stored object
destruct()440 void destruct() { get().T::~T(); }
441 };
442
443 template<class T>
makeOptional(T && t)444 Optional<typename std::decay<T>::type> makeOptional(T&& t) {
445 return Optional<typename std::decay<T>::type>(std::forward<T>(t));
446 }
447
448 template<class T, class... Args>
makeOptional(Args &&...args)449 Optional<typename std::decay<T>::type> makeOptional(Args&&... args) {
450 return Optional<typename std::decay<T>::type>(kInplace,
451 std::forward<Args>(args)...);
452 }
453
454 template<class T>
455 bool operator==(const Optional<T>& l, const Optional<T>& r) {
456 return l.hasValue() ? r.hasValue() && *l == *r : !r.hasValue();
457 }
458 template<class T>
459 bool operator==(const Optional<T>& l, NulloptT) {
460 return !l;
461 }
462 template<class T>
463 bool operator==(NulloptT, const Optional<T>& r) {
464 return !r;
465 }
466 template<class T>
467 bool operator==(const Optional<T>& l, const T& r) {
468 return bool(l) && *l == r;
469 }
470 template<class T>
471 bool operator==(const T& l, const Optional<T>& r) {
472 return bool(r) && l == *r;
473 }
474
475 template<class T>
476 bool operator!=(const Optional<T>& l, const Optional<T>& r) {
477 return !(l == r);
478 }
479 template<class T>
480 bool operator!=(const Optional<T>& l, NulloptT) {
481 return bool(l);
482 }
483 template<class T>
484 bool operator!=(NulloptT, const Optional<T>& r) {
485 return bool(r);
486 }
487 template<class T>
488 bool operator!=(const Optional<T>& l, const T& r) {
489 return !l || !(*l == r);
490 }
491 template<class T>
492 bool operator!=(const T& l, const Optional<T>& r) {
493 return !r || !(l == *r);
494 }
495
496 template<class T>
497 bool operator<(const Optional<T>& l, const Optional<T>& r) {
498 return !r ? false : (!l ? true : *l < *r);
499 }
500 template<class T>
501 bool operator<(const Optional<T>&, NulloptT) {
502 return false;
503 }
504 template<class T>
505 bool operator<(NulloptT, const Optional<T>& r) {
506 return bool(r);
507 }
508 template<class T>
509 bool operator<(const Optional<T>& l, const T& r) {
510 return !l || *l < r;
511 }
512 template<class T>
513 bool operator<(const T& l, const Optional<T>& r) {
514 return bool(r) && l < *r;
515 }
516
517 } // namespace base
518 } // namespace astc_codec
519
520 #endif // ASTC_CODEC_BASE_OPTIONAL_H_
521