Home | History | Annotate | Download | only in base
      1 // Copyright 2016 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef BASE_OPTIONAL_H_
      6 #define BASE_OPTIONAL_H_
      7 
      8 #include <type_traits>
      9 
     10 #include "base/logging.h"
     11 #include "base/memory/aligned_memory.h"
     12 #include "base/template_util.h"
     13 
     14 namespace base {
     15 
     16 // Specification:
     17 // http://en.cppreference.com/w/cpp/utility/optional/in_place_t
     18 struct in_place_t {};
     19 
     20 // Specification:
     21 // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
     22 struct nullopt_t {
     23   constexpr explicit nullopt_t(int) {}
     24 };
     25 
     26 // Specification:
     27 // http://en.cppreference.com/w/cpp/utility/optional/in_place
     28 constexpr in_place_t in_place = {};
     29 
     30 // Specification:
     31 // http://en.cppreference.com/w/cpp/utility/optional/nullopt
     32 constexpr nullopt_t nullopt(0);
     33 
     34 namespace internal {
     35 
     36 template <typename T, bool = base::is_trivially_destructible<T>::value>
     37 struct OptionalStorage {
     38   // When T is not trivially destructible we must call its
     39   // destructor before deallocating its memory.
     40   ~OptionalStorage() {
     41     if (!is_null_)
     42       buffer_.template data_as<T>()->~T();
     43   }
     44 
     45   bool is_null_ = true;
     46   base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_;
     47 };
     48 
     49 template <typename T>
     50 struct OptionalStorage<T, true> {
     51   // When T is trivially destructible (i.e. its destructor does nothing)
     52   // there is no need to call it.
     53   // Since |base::AlignedMemory| is just an array its destructor
     54   // is trivial. Explicitly defaulting the destructor means it's not
     55   // user-provided. All of this together make this destructor trivial.
     56   ~OptionalStorage() = default;
     57 
     58   bool is_null_ = true;
     59   base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_;
     60 };
     61 
     62 }  // namespace internal
     63 
     64 // base::Optional is a Chromium version of the C++17 optional class:
     65 // std::optional documentation:
     66 // http://en.cppreference.com/w/cpp/utility/optional
     67 // Chromium documentation:
     68 // https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
     69 //
     70 // These are the differences between the specification and the implementation:
     71 // - The constructor and emplace method using initializer_list are not
     72 //   implemented because 'initializer_list' is banned from Chromium.
     73 // - Constructors do not use 'constexpr' as it is a C++14 extension.
     74 // - 'constexpr' might be missing in some places for reasons specified locally.
     75 // - No exceptions are thrown, because they are banned from Chromium.
     76 // - All the non-members are in the 'base' namespace instead of 'std'.
     77 template <typename T>
     78 class Optional {
     79  public:
     80   using value_type = T;
     81 
     82   constexpr Optional() = default;
     83   Optional(base::nullopt_t) : Optional() {}
     84 
     85   Optional(const Optional& other) {
     86     if (!other.storage_.is_null_)
     87       Init(other.value());
     88   }
     89 
     90   Optional(Optional&& other) {
     91     if (!other.storage_.is_null_)
     92       Init(std::move(other.value()));
     93   }
     94 
     95   Optional(const T& value) { Init(value); }
     96 
     97   Optional(T&& value) { Init(std::move(value)); }
     98 
     99   template <class... Args>
    100   explicit Optional(base::in_place_t, Args&&... args) {
    101     emplace(std::forward<Args>(args)...);
    102   }
    103 
    104   ~Optional() = default;
    105 
    106   Optional& operator=(base::nullopt_t) {
    107     FreeIfNeeded();
    108     return *this;
    109   }
    110 
    111   Optional& operator=(const Optional& other) {
    112     if (other.storage_.is_null_) {
    113       FreeIfNeeded();
    114       return *this;
    115     }
    116 
    117     InitOrAssign(other.value());
    118     return *this;
    119   }
    120 
    121   Optional& operator=(Optional&& other) {
    122     if (other.storage_.is_null_) {
    123       FreeIfNeeded();
    124       return *this;
    125     }
    126 
    127     InitOrAssign(std::move(other.value()));
    128     return *this;
    129   }
    130 
    131   template <class U>
    132   typename std::enable_if<std::is_same<std::decay<U>, T>::value,
    133                           Optional&>::type
    134   operator=(U&& value) {
    135     InitOrAssign(std::forward<U>(value));
    136     return *this;
    137   }
    138 
    139   // TODO(mlamouri): can't use 'constexpr' with DCHECK.
    140   const T* operator->() const {
    141     DCHECK(!storage_.is_null_);
    142     return &value();
    143   }
    144 
    145   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
    146   // meant to be 'constexpr const'.
    147   T* operator->() {
    148     DCHECK(!storage_.is_null_);
    149     return &value();
    150   }
    151 
    152   constexpr const T& operator*() const& { return value(); }
    153 
    154   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
    155   // meant to be 'constexpr const'.
    156   T& operator*() & { return value(); }
    157 
    158   constexpr const T&& operator*() const&& { return std::move(value()); }
    159 
    160   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
    161   // meant to be 'constexpr const'.
    162   T&& operator*() && { return std::move(value()); }
    163 
    164   constexpr explicit operator bool() const { return !storage_.is_null_; }
    165 
    166   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
    167   // meant to be 'constexpr const'.
    168   T& value() & {
    169     DCHECK(!storage_.is_null_);
    170     return *storage_.buffer_.template data_as<T>();
    171   }
    172 
    173   // TODO(mlamouri): can't use 'constexpr' with DCHECK.
    174   const T& value() const& {
    175     DCHECK(!storage_.is_null_);
    176     return *storage_.buffer_.template data_as<T>();
    177   }
    178 
    179   // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
    180   // meant to be 'constexpr const'.
    181   T&& value() && {
    182     DCHECK(!storage_.is_null_);
    183     return std::move(*storage_.buffer_.template data_as<T>());
    184   }
    185 
    186   // TODO(mlamouri): can't use 'constexpr' with DCHECK.
    187   const T&& value() const&& {
    188     DCHECK(!storage_.is_null_);
    189     return std::move(*storage_.buffer_.template data_as<T>());
    190   }
    191 
    192   template <class U>
    193   constexpr T value_or(U&& default_value) const& {
    194     // TODO(mlamouri): add the following assert when possible:
    195     // static_assert(std::is_copy_constructible<T>::value,
    196     //               "T must be copy constructible");
    197     static_assert(std::is_convertible<U, T>::value,
    198                   "U must be convertible to T");
    199     return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
    200                              : value();
    201   }
    202 
    203   template <class U>
    204   T value_or(U&& default_value) && {
    205     // TODO(mlamouri): add the following assert when possible:
    206     // static_assert(std::is_move_constructible<T>::value,
    207     //               "T must be move constructible");
    208     static_assert(std::is_convertible<U, T>::value,
    209                   "U must be convertible to T");
    210     return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
    211                              : std::move(value());
    212   }
    213 
    214   void swap(Optional& other) {
    215     if (storage_.is_null_ && other.storage_.is_null_)
    216       return;
    217 
    218     if (storage_.is_null_ != other.storage_.is_null_) {
    219       if (storage_.is_null_) {
    220         Init(std::move(*other.storage_.buffer_.template data_as<T>()));
    221         other.FreeIfNeeded();
    222       } else {
    223         other.Init(std::move(*storage_.buffer_.template data_as<T>()));
    224         FreeIfNeeded();
    225       }
    226       return;
    227     }
    228 
    229     DCHECK(!storage_.is_null_ && !other.storage_.is_null_);
    230     using std::swap;
    231     swap(**this, *other);
    232   }
    233 
    234   template <class... Args>
    235   void emplace(Args&&... args) {
    236     FreeIfNeeded();
    237     Init(std::forward<Args>(args)...);
    238   }
    239 
    240  private:
    241   void Init(const T& value) {
    242     DCHECK(storage_.is_null_);
    243     new (storage_.buffer_.void_data()) T(value);
    244     storage_.is_null_ = false;
    245   }
    246 
    247   void Init(T&& value) {
    248     DCHECK(storage_.is_null_);
    249     new (storage_.buffer_.void_data()) T(std::move(value));
    250     storage_.is_null_ = false;
    251   }
    252 
    253   template <class... Args>
    254   void Init(Args&&... args) {
    255     DCHECK(storage_.is_null_);
    256     new (storage_.buffer_.void_data()) T(std::forward<Args>(args)...);
    257     storage_.is_null_ = false;
    258   }
    259 
    260   void InitOrAssign(const T& value) {
    261     if (storage_.is_null_)
    262       Init(value);
    263     else
    264       *storage_.buffer_.template data_as<T>() = value;
    265   }
    266 
    267   void InitOrAssign(T&& value) {
    268     if (storage_.is_null_)
    269       Init(std::move(value));
    270     else
    271       *storage_.buffer_.template data_as<T>() = std::move(value);
    272   }
    273 
    274   void FreeIfNeeded() {
    275     if (storage_.is_null_)
    276       return;
    277     storage_.buffer_.template data_as<T>()->~T();
    278     storage_.is_null_ = true;
    279   }
    280 
    281   internal::OptionalStorage<T> storage_;
    282 };
    283 
    284 template <class T>
    285 constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
    286   return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
    287 }
    288 
    289 template <class T>
    290 constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) {
    291   return !(lhs == rhs);
    292 }
    293 
    294 template <class T>
    295 constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) {
    296   return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs);
    297 }
    298 
    299 template <class T>
    300 constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) {
    301   return !(rhs < lhs);
    302 }
    303 
    304 template <class T>
    305 constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) {
    306   return rhs < lhs;
    307 }
    308 
    309 template <class T>
    310 constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) {
    311   return !(lhs < rhs);
    312 }
    313 
    314 template <class T>
    315 constexpr bool operator==(const Optional<T>& opt, base::nullopt_t) {
    316   return !opt;
    317 }
    318 
    319 template <class T>
    320 constexpr bool operator==(base::nullopt_t, const Optional<T>& opt) {
    321   return !opt;
    322 }
    323 
    324 template <class T>
    325 constexpr bool operator!=(const Optional<T>& opt, base::nullopt_t) {
    326   return !!opt;
    327 }
    328 
    329 template <class T>
    330 constexpr bool operator!=(base::nullopt_t, const Optional<T>& opt) {
    331   return !!opt;
    332 }
    333 
    334 template <class T>
    335 constexpr bool operator<(const Optional<T>& opt, base::nullopt_t) {
    336   return false;
    337 }
    338 
    339 template <class T>
    340 constexpr bool operator<(base::nullopt_t, const Optional<T>& opt) {
    341   return !!opt;
    342 }
    343 
    344 template <class T>
    345 constexpr bool operator<=(const Optional<T>& opt, base::nullopt_t) {
    346   return !opt;
    347 }
    348 
    349 template <class T>
    350 constexpr bool operator<=(base::nullopt_t, const Optional<T>& opt) {
    351   return true;
    352 }
    353 
    354 template <class T>
    355 constexpr bool operator>(const Optional<T>& opt, base::nullopt_t) {
    356   return !!opt;
    357 }
    358 
    359 template <class T>
    360 constexpr bool operator>(base::nullopt_t, const Optional<T>& opt) {
    361   return false;
    362 }
    363 
    364 template <class T>
    365 constexpr bool operator>=(const Optional<T>& opt, base::nullopt_t) {
    366   return true;
    367 }
    368 
    369 template <class T>
    370 constexpr bool operator>=(base::nullopt_t, const Optional<T>& opt) {
    371   return !opt;
    372 }
    373 
    374 template <class T>
    375 constexpr bool operator==(const Optional<T>& opt, const T& value) {
    376   return opt != nullopt ? *opt == value : false;
    377 }
    378 
    379 template <class T>
    380 constexpr bool operator==(const T& value, const Optional<T>& opt) {
    381   return opt == value;
    382 }
    383 
    384 template <class T>
    385 constexpr bool operator!=(const Optional<T>& opt, const T& value) {
    386   return !(opt == value);
    387 }
    388 
    389 template <class T>
    390 constexpr bool operator!=(const T& value, const Optional<T>& opt) {
    391   return !(opt == value);
    392 }
    393 
    394 template <class T>
    395 constexpr bool operator<(const Optional<T>& opt, const T& value) {
    396   return opt != nullopt ? *opt < value : true;
    397 }
    398 
    399 template <class T>
    400 constexpr bool operator<(const T& value, const Optional<T>& opt) {
    401   return opt != nullopt ? value < *opt : false;
    402 }
    403 
    404 template <class T>
    405 constexpr bool operator<=(const Optional<T>& opt, const T& value) {
    406   return !(opt > value);
    407 }
    408 
    409 template <class T>
    410 constexpr bool operator<=(const T& value, const Optional<T>& opt) {
    411   return !(value > opt);
    412 }
    413 
    414 template <class T>
    415 constexpr bool operator>(const Optional<T>& opt, const T& value) {
    416   return value < opt;
    417 }
    418 
    419 template <class T>
    420 constexpr bool operator>(const T& value, const Optional<T>& opt) {
    421   return opt < value;
    422 }
    423 
    424 template <class T>
    425 constexpr bool operator>=(const Optional<T>& opt, const T& value) {
    426   return !(opt < value);
    427 }
    428 
    429 template <class T>
    430 constexpr bool operator>=(const T& value, const Optional<T>& opt) {
    431   return !(value < opt);
    432 }
    433 
    434 template <class T>
    435 constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
    436   return Optional<typename std::decay<T>::type>(std::forward<T>(value));
    437 }
    438 
    439 template <class T>
    440 void swap(Optional<T>& lhs, Optional<T>& rhs) {
    441   lhs.swap(rhs);
    442 }
    443 
    444 }  // namespace base
    445 
    446 namespace std {
    447 
    448 template <class T>
    449 struct hash<base::Optional<T>> {
    450   size_t operator()(const base::Optional<T>& opt) const {
    451     return opt == base::nullopt ? 0 : std::hash<T>()(*opt);
    452   }
    453 };
    454 
    455 }  // namespace std
    456 
    457 #endif  // BASE_OPTIONAL_H_
    458