• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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