• 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 #ifndef BASE_OPTIONAL_H_
6 #define BASE_OPTIONAL_H_
7 
8 #include <type_traits>
9 
10 #include "base/logging.h"
11 #include "base/template_util.h"
12 
13 namespace base {
14 
15 // Specification:
16 // http://en.cppreference.com/w/cpp/utility/optional/in_place_t
17 struct in_place_t {};
18 
19 // Specification:
20 // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
21 struct nullopt_t {
nullopt_tnullopt_t22   constexpr explicit nullopt_t(int) {}
23 };
24 
25 // Specification:
26 // http://en.cppreference.com/w/cpp/utility/optional/in_place
27 constexpr in_place_t in_place = {};
28 
29 // Specification:
30 // http://en.cppreference.com/w/cpp/utility/optional/nullopt
31 constexpr nullopt_t nullopt(0);
32 
33 namespace internal {
34 
35 template <typename T, bool = base::is_trivially_destructible<T>::value>
36 struct OptionalStorage {
37   // Initializing |empty_| here instead of using default member initializing
38   // to avoid errors in g++ 4.8.
OptionalStorageOptionalStorage39   constexpr OptionalStorage() : empty_('\0') {}
40 
OptionalStorageOptionalStorage41   constexpr explicit OptionalStorage(const T& value)
42       : is_null_(false), value_(value) {}
43 
44   // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
OptionalStorageOptionalStorage45   explicit OptionalStorage(T&& value)
46       : is_null_(false), value_(std::move(value)) {}
47 
48   // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
49   template <class... Args>
OptionalStorageOptionalStorage50   explicit OptionalStorage(base::in_place_t, Args&&... args)
51       : is_null_(false), value_(std::forward<Args>(args)...) {}
52 
53   // When T is not trivially destructible we must call its
54   // destructor before deallocating its memory.
~OptionalStorageOptionalStorage55   ~OptionalStorage() {
56     if (!is_null_)
57       value_.~T();
58   }
59 
60   bool is_null_ = true;
61   union {
62     // |empty_| exists so that the union will always be initialized, even when
63     // it doesn't contain a value. Union members must be initialized for the
64     // constructor to be 'constexpr'.
65     char empty_;
66     T value_;
67   };
68 };
69 
70 template <typename T>
71 struct OptionalStorage<T, true> {
72   // Initializing |empty_| here instead of using default member initializing
73   // to avoid errors in g++ 4.8.
74   constexpr OptionalStorage() : empty_('\0') {}
75 
76   constexpr explicit OptionalStorage(const T& value)
77       : is_null_(false), value_(value) {}
78 
79   // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
80   explicit OptionalStorage(T&& value)
81       : is_null_(false), value_(std::move(value)) {}
82 
83   // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
84   template <class... Args>
85   explicit OptionalStorage(base::in_place_t, Args&&... args)
86       : is_null_(false), value_(std::forward<Args>(args)...) {}
87 
88   // When T is trivially destructible (i.e. its destructor does nothing) there
89   // is no need to call it. Explicitly defaulting the destructor means it's not
90   // user-provided. Those two together make this destructor trivial.
91   ~OptionalStorage() = default;
92 
93   bool is_null_ = true;
94   union {
95     // |empty_| exists so that the union will always be initialized, even when
96     // it doesn't contain a value. Union members must be initialized for the
97     // constructor to be 'constexpr'.
98     char empty_;
99     T value_;
100   };
101 };
102 
103 }  // namespace internal
104 
105 // base::Optional is a Chromium version of the C++17 optional class:
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 'base' namespace instead of 'std'.
118 template <typename T>
119 class Optional {
120  public:
121   using value_type = T;
122 
123   constexpr Optional() {}
124 
125   constexpr Optional(base::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(base::in_place_t, Args&&... args)
145       : storage_(base::in_place, std::forward<Args>(args)...) {}
146 
147   ~Optional() = default;
148 
149   Optional& operator=(base::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, base::nullopt_t) {
365   return !opt;
366 }
367 
368 template <class T>
369 constexpr bool operator==(base::nullopt_t, const Optional<T>& opt) {
370   return !opt;
371 }
372 
373 template <class T>
374 constexpr bool operator!=(const Optional<T>& opt, base::nullopt_t) {
375   return !!opt;
376 }
377 
378 template <class T>
379 constexpr bool operator!=(base::nullopt_t, const Optional<T>& opt) {
380   return !!opt;
381 }
382 
383 template <class T>
384 constexpr bool operator<(const Optional<T>& opt, base::nullopt_t) {
385   return false;
386 }
387 
388 template <class T>
389 constexpr bool operator<(base::nullopt_t, const Optional<T>& opt) {
390   return !!opt;
391 }
392 
393 template <class T>
394 constexpr bool operator<=(const Optional<T>& opt, base::nullopt_t) {
395   return !opt;
396 }
397 
398 template <class T>
399 constexpr bool operator<=(base::nullopt_t, const Optional<T>& opt) {
400   return true;
401 }
402 
403 template <class T>
404 constexpr bool operator>(const Optional<T>& opt, base::nullopt_t) {
405   return !!opt;
406 }
407 
408 template <class T>
409 constexpr bool operator>(base::nullopt_t, const Optional<T>& opt) {
410   return false;
411 }
412 
413 template <class T>
414 constexpr bool operator>=(const Optional<T>& opt, base::nullopt_t) {
415   return true;
416 }
417 
418 template <class T>
419 constexpr bool operator>=(base::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 base
494 
495 namespace std {
496 
497 template <class T>
498 struct hash<base::Optional<T>> {
499   size_t operator()(const base::Optional<T>& opt) const {
500     return opt == base::nullopt ? 0 : std::hash<T>()(*opt);
501   }
502 };
503 
504 }  // namespace std
505 
506 #endif  // BASE_OPTIONAL_H_
507