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