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