blob: e95bf6c9a13ceabb8e4ab7935a1c74901e90e20e [file] [log] [blame]
mlamouri53f6b252016-04-19 17:27:011// 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"
mlamouri53f6b252016-04-19 17:27:0111
12namespace base {
13
14// Specification:
15// http://en.cppreference.com/w/cpp/utility/optional/in_place_t
16struct in_place_t {};
17
18// Specification:
19// http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
20struct nullopt_t {
21 constexpr explicit nullopt_t(int) {}
22};
23
24// Specification:
25// http://en.cppreference.com/w/cpp/utility/optional/in_place
26constexpr in_place_t in_place = {};
27
28// Specification:
29// http://en.cppreference.com/w/cpp/utility/optional/nullopt
30constexpr nullopt_t nullopt(0);
31
alshabalinf06b07df2016-05-27 08:01:3132namespace internal {
33
danakj90acaf8292017-04-05 17:59:2734template <typename T, bool = std::is_trivially_destructible<T>::value>
alshabalinf06b07df2016-05-27 08:01:3135struct OptionalStorage {
alshabalina637f252016-10-26 22:29:2836 // Initializing |empty_| here instead of using default member initializing
37 // to avoid errors in g++ 4.8.
38 constexpr OptionalStorage() : empty_('\0') {}
alshabalin9494e4542016-10-25 09:56:4139
40 constexpr explicit OptionalStorage(const T& value)
41 : is_null_(false), value_(value) {}
42
Daniel Cheng429dbdc52017-10-04 15:33:5643 constexpr explicit OptionalStorage(T&& value)
alshabalin9494e4542016-10-25 09:56:4144 : is_null_(false), value_(std::move(value)) {}
45
alshabalin9494e4542016-10-25 09:56:4146 template <class... Args>
Daniel Cheng429dbdc52017-10-04 15:33:5647 constexpr explicit OptionalStorage(base::in_place_t, Args&&... args)
alshabalin9494e4542016-10-25 09:56:4148 : is_null_(false), value_(std::forward<Args>(args)...) {}
49
alshabalinf06b07df2016-05-27 08:01:3150 // When T is not trivially destructible we must call its
51 // destructor before deallocating its memory.
52 ~OptionalStorage() {
53 if (!is_null_)
kwiberg882859a2016-08-16 09:42:2154 value_.~T();
alshabalinf06b07df2016-05-27 08:01:3155 }
56
57 bool is_null_ = true;
kwiberg882859a2016-08-16 09:42:2158 union {
59 // |empty_| exists so that the union will always be initialized, even when
alshabalina637f252016-10-26 22:29:2860 // it doesn't contain a value. Union members must be initialized for the
61 // constructor to be 'constexpr'.
62 char empty_;
kwiberg882859a2016-08-16 09:42:2163 T value_;
64 };
alshabalinf06b07df2016-05-27 08:01:3165};
66
67template <typename T>
68struct OptionalStorage<T, true> {
alshabalina637f252016-10-26 22:29:2869 // Initializing |empty_| here instead of using default member initializing
70 // to avoid errors in g++ 4.8.
71 constexpr OptionalStorage() : empty_('\0') {}
alshabalin9494e4542016-10-25 09:56:4172
73 constexpr explicit OptionalStorage(const T& value)
74 : is_null_(false), value_(value) {}
75
Daniel Cheng429dbdc52017-10-04 15:33:5676 constexpr explicit OptionalStorage(T&& value)
alshabalin9494e4542016-10-25 09:56:4177 : is_null_(false), value_(std::move(value)) {}
78
alshabalin9494e4542016-10-25 09:56:4179 template <class... Args>
Daniel Cheng429dbdc52017-10-04 15:33:5680 constexpr explicit OptionalStorage(base::in_place_t, Args&&... args)
alshabalin9494e4542016-10-25 09:56:4181 : is_null_(false), value_(std::forward<Args>(args)...) {}
82
kwiberg882859a2016-08-16 09:42:2183 // When T is trivially destructible (i.e. its destructor does nothing) there
84 // is no need to call it. Explicitly defaulting the destructor means it's not
85 // user-provided. Those two together make this destructor trivial.
alshabalinf06b07df2016-05-27 08:01:3186 ~OptionalStorage() = default;
87
88 bool is_null_ = true;
kwiberg882859a2016-08-16 09:42:2189 union {
90 // |empty_| exists so that the union will always be initialized, even when
alshabalina637f252016-10-26 22:29:2891 // it doesn't contain a value. Union members must be initialized for the
92 // constructor to be 'constexpr'.
93 char empty_;
kwiberg882859a2016-08-16 09:42:2194 T value_;
95 };
alshabalinf06b07df2016-05-27 08:01:3196};
97
98} // namespace internal
99
mlamouri53f6b252016-04-19 17:27:01100// base::Optional is a Chromium version of the C++17 optional class:
101// std::optional documentation:
102// http://en.cppreference.com/w/cpp/utility/optional
103// Chromium documentation:
104// https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
105//
106// These are the differences between the specification and the implementation:
107// - The constructor and emplace method using initializer_list are not
108// implemented because 'initializer_list' is banned from Chromium.
109// - Constructors do not use 'constexpr' as it is a C++14 extension.
110// - 'constexpr' might be missing in some places for reasons specified locally.
111// - No exceptions are thrown, because they are banned from Chromium.
112// - All the non-members are in the 'base' namespace instead of 'std'.
113template <typename T>
114class Optional {
115 public:
alshabalinf06b07df2016-05-27 08:01:31116 using value_type = T;
117
Chris Watkins091d6292017-12-13 04:25:58118 constexpr Optional() = default;
alshabalin9494e4542016-10-25 09:56:41119
120 constexpr Optional(base::nullopt_t) {}
mlamouri53f6b252016-04-19 17:27:01121
Daniel Cheng429dbdc52017-10-04 15:33:56122 // TODO(dcheng): Make these constexpr iff T is trivially constructible.
mlamouri53f6b252016-04-19 17:27:01123 Optional(const Optional& other) {
alshabalinf06b07df2016-05-27 08:01:31124 if (!other.storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01125 Init(other.value());
126 }
127
128 Optional(Optional&& other) {
alshabalinf06b07df2016-05-27 08:01:31129 if (!other.storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01130 Init(std::move(other.value()));
131 }
132
alshabalin9494e4542016-10-25 09:56:41133 constexpr Optional(const T& value) : storage_(value) {}
mlamouri53f6b252016-04-19 17:27:01134
Daniel Cheng429dbdc52017-10-04 15:33:56135 constexpr Optional(T&& value) : storage_(std::move(value)) {}
mlamouri53f6b252016-04-19 17:27:01136
137 template <class... Args>
Daniel Cheng429dbdc52017-10-04 15:33:56138 constexpr explicit Optional(base::in_place_t, Args&&... args)
alshabalin9494e4542016-10-25 09:56:41139 : storage_(base::in_place, std::forward<Args>(args)...) {}
mlamouri53f6b252016-04-19 17:27:01140
alshabalinf06b07df2016-05-27 08:01:31141 ~Optional() = default;
mlamouri53f6b252016-04-19 17:27:01142
143 Optional& operator=(base::nullopt_t) {
144 FreeIfNeeded();
145 return *this;
146 }
147
148 Optional& operator=(const Optional& other) {
alshabalinf06b07df2016-05-27 08:01:31149 if (other.storage_.is_null_) {
mlamouri53f6b252016-04-19 17:27:01150 FreeIfNeeded();
151 return *this;
152 }
153
154 InitOrAssign(other.value());
155 return *this;
156 }
157
158 Optional& operator=(Optional&& other) {
alshabalinf06b07df2016-05-27 08:01:31159 if (other.storage_.is_null_) {
mlamouri53f6b252016-04-19 17:27:01160 FreeIfNeeded();
161 return *this;
162 }
163
164 InitOrAssign(std::move(other.value()));
165 return *this;
166 }
167
168 template <class U>
Andrey Kraynov976fbbd2017-09-13 17:15:44169 typename std::enable_if<std::is_same<std::decay_t<U>, T>::value,
mlamouri53f6b252016-04-19 17:27:01170 Optional&>::type
171 operator=(U&& value) {
172 InitOrAssign(std::forward<U>(value));
173 return *this;
174 }
175
Daniel Cheng429dbdc52017-10-04 15:33:56176 constexpr const T* operator->() const {
alshabalinf06b07df2016-05-27 08:01:31177 DCHECK(!storage_.is_null_);
mlamouri53f6b252016-04-19 17:27:01178 return &value();
179 }
180
Daniel Cheng429dbdc52017-10-04 15:33:56181 constexpr T* operator->() {
alshabalinf06b07df2016-05-27 08:01:31182 DCHECK(!storage_.is_null_);
mlamouri53f6b252016-04-19 17:27:01183 return &value();
184 }
185
186 constexpr const T& operator*() const& { return value(); }
187
Daniel Cheng429dbdc52017-10-04 15:33:56188 constexpr T& operator*() & { return value(); }
mlamouri53f6b252016-04-19 17:27:01189
190 constexpr const T&& operator*() const&& { return std::move(value()); }
191
Daniel Cheng429dbdc52017-10-04 15:33:56192 constexpr T&& operator*() && { return std::move(value()); }
mlamouri53f6b252016-04-19 17:27:01193
alshabalinf06b07df2016-05-27 08:01:31194 constexpr explicit operator bool() const { return !storage_.is_null_; }
mlamouri53f6b252016-04-19 17:27:01195
mlamouri26204572016-08-10 12:24:15196 constexpr bool has_value() const { return !storage_.is_null_; }
197
Daniel Cheng429dbdc52017-10-04 15:33:56198 constexpr T& value() & {
alshabalinf06b07df2016-05-27 08:01:31199 DCHECK(!storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21200 return storage_.value_;
mlamouri53f6b252016-04-19 17:27:01201 }
202
Daniel Cheng429dbdc52017-10-04 15:33:56203 constexpr const T& value() const & {
alshabalinf06b07df2016-05-27 08:01:31204 DCHECK(!storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21205 return storage_.value_;
mlamouri53f6b252016-04-19 17:27:01206 }
207
Daniel Cheng429dbdc52017-10-04 15:33:56208 constexpr T&& value() && {
alshabalinf06b07df2016-05-27 08:01:31209 DCHECK(!storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21210 return std::move(storage_.value_);
mlamouri53f6b252016-04-19 17:27:01211 }
212
Daniel Cheng429dbdc52017-10-04 15:33:56213 constexpr const T&& value() const && {
alshabalinf06b07df2016-05-27 08:01:31214 DCHECK(!storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21215 return std::move(storage_.value_);
mlamouri53f6b252016-04-19 17:27:01216 }
217
218 template <class U>
219 constexpr T value_or(U&& default_value) const& {
220 // TODO(mlamouri): add the following assert when possible:
221 // static_assert(std::is_copy_constructible<T>::value,
222 // "T must be copy constructible");
223 static_assert(std::is_convertible<U, T>::value,
224 "U must be convertible to T");
alshabalinf06b07df2016-05-27 08:01:31225 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
226 : value();
mlamouri53f6b252016-04-19 17:27:01227 }
228
229 template <class U>
230 T value_or(U&& default_value) && {
231 // TODO(mlamouri): add the following assert when possible:
232 // static_assert(std::is_move_constructible<T>::value,
233 // "T must be move constructible");
234 static_assert(std::is_convertible<U, T>::value,
235 "U must be convertible to T");
alshabalinf06b07df2016-05-27 08:01:31236 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
237 : std::move(value());
mlamouri53f6b252016-04-19 17:27:01238 }
239
240 void swap(Optional& other) {
alshabalinf06b07df2016-05-27 08:01:31241 if (storage_.is_null_ && other.storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01242 return;
243
alshabalinf06b07df2016-05-27 08:01:31244 if (storage_.is_null_ != other.storage_.is_null_) {
245 if (storage_.is_null_) {
kwiberg882859a2016-08-16 09:42:21246 Init(std::move(other.storage_.value_));
mlamouri53f6b252016-04-19 17:27:01247 other.FreeIfNeeded();
248 } else {
kwiberg882859a2016-08-16 09:42:21249 other.Init(std::move(storage_.value_));
mlamouri53f6b252016-04-19 17:27:01250 FreeIfNeeded();
251 }
252 return;
253 }
254
alshabalinf06b07df2016-05-27 08:01:31255 DCHECK(!storage_.is_null_ && !other.storage_.is_null_);
mlamouri53f6b252016-04-19 17:27:01256 using std::swap;
257 swap(**this, *other);
258 }
259
mlamouri26204572016-08-10 12:24:15260 void reset() {
261 FreeIfNeeded();
262 }
263
mlamouri53f6b252016-04-19 17:27:01264 template <class... Args>
265 void emplace(Args&&... args) {
266 FreeIfNeeded();
267 Init(std::forward<Args>(args)...);
268 }
269
270 private:
271 void Init(const T& value) {
alshabalinf06b07df2016-05-27 08:01:31272 DCHECK(storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21273 new (&storage_.value_) T(value);
alshabalinf06b07df2016-05-27 08:01:31274 storage_.is_null_ = false;
mlamouri53f6b252016-04-19 17:27:01275 }
276
277 void Init(T&& value) {
alshabalinf06b07df2016-05-27 08:01:31278 DCHECK(storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21279 new (&storage_.value_) T(std::move(value));
alshabalinf06b07df2016-05-27 08:01:31280 storage_.is_null_ = false;
mlamouri53f6b252016-04-19 17:27:01281 }
282
283 template <class... Args>
284 void Init(Args&&... args) {
alshabalinf06b07df2016-05-27 08:01:31285 DCHECK(storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21286 new (&storage_.value_) T(std::forward<Args>(args)...);
alshabalinf06b07df2016-05-27 08:01:31287 storage_.is_null_ = false;
mlamouri53f6b252016-04-19 17:27:01288 }
289
290 void InitOrAssign(const T& value) {
alshabalinf06b07df2016-05-27 08:01:31291 if (storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01292 Init(value);
293 else
kwiberg882859a2016-08-16 09:42:21294 storage_.value_ = value;
mlamouri53f6b252016-04-19 17:27:01295 }
296
297 void InitOrAssign(T&& value) {
alshabalinf06b07df2016-05-27 08:01:31298 if (storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01299 Init(std::move(value));
300 else
kwiberg882859a2016-08-16 09:42:21301 storage_.value_ = std::move(value);
mlamouri53f6b252016-04-19 17:27:01302 }
303
304 void FreeIfNeeded() {
alshabalinf06b07df2016-05-27 08:01:31305 if (storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01306 return;
kwiberg882859a2016-08-16 09:42:21307 storage_.value_.~T();
alshabalinf06b07df2016-05-27 08:01:31308 storage_.is_null_ = true;
mlamouri53f6b252016-04-19 17:27:01309 }
310
alshabalinf06b07df2016-05-27 08:01:31311 internal::OptionalStorage<T> storage_;
mlamouri53f6b252016-04-19 17:27:01312};
313
314template <class T>
315constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
316 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
317}
318
319template <class T>
320constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) {
321 return !(lhs == rhs);
322}
323
324template <class T>
325constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) {
326 return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs);
327}
328
329template <class T>
330constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) {
331 return !(rhs < lhs);
332}
333
334template <class T>
335constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) {
336 return rhs < lhs;
337}
338
339template <class T>
340constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) {
341 return !(lhs < rhs);
342}
343
344template <class T>
345constexpr bool operator==(const Optional<T>& opt, base::nullopt_t) {
346 return !opt;
347}
348
349template <class T>
350constexpr bool operator==(base::nullopt_t, const Optional<T>& opt) {
351 return !opt;
352}
353
354template <class T>
355constexpr bool operator!=(const Optional<T>& opt, base::nullopt_t) {
356 return !!opt;
357}
358
359template <class T>
360constexpr bool operator!=(base::nullopt_t, const Optional<T>& opt) {
361 return !!opt;
362}
363
364template <class T>
365constexpr bool operator<(const Optional<T>& opt, base::nullopt_t) {
366 return false;
367}
368
369template <class T>
370constexpr bool operator<(base::nullopt_t, const Optional<T>& opt) {
371 return !!opt;
372}
373
374template <class T>
375constexpr bool operator<=(const Optional<T>& opt, base::nullopt_t) {
376 return !opt;
377}
378
379template <class T>
380constexpr bool operator<=(base::nullopt_t, const Optional<T>& opt) {
381 return true;
382}
383
384template <class T>
385constexpr bool operator>(const Optional<T>& opt, base::nullopt_t) {
386 return !!opt;
387}
388
389template <class T>
390constexpr bool operator>(base::nullopt_t, const Optional<T>& opt) {
391 return false;
392}
393
394template <class T>
395constexpr bool operator>=(const Optional<T>& opt, base::nullopt_t) {
396 return true;
397}
398
399template <class T>
400constexpr bool operator>=(base::nullopt_t, const Optional<T>& opt) {
401 return !opt;
402}
403
404template <class T>
405constexpr bool operator==(const Optional<T>& opt, const T& value) {
406 return opt != nullopt ? *opt == value : false;
407}
408
409template <class T>
410constexpr bool operator==(const T& value, const Optional<T>& opt) {
411 return opt == value;
412}
413
414template <class T>
415constexpr bool operator!=(const Optional<T>& opt, const T& value) {
416 return !(opt == value);
417}
418
419template <class T>
420constexpr bool operator!=(const T& value, const Optional<T>& opt) {
421 return !(opt == value);
422}
423
424template <class T>
425constexpr bool operator<(const Optional<T>& opt, const T& value) {
426 return opt != nullopt ? *opt < value : true;
427}
428
429template <class T>
430constexpr bool operator<(const T& value, const Optional<T>& opt) {
431 return opt != nullopt ? value < *opt : false;
432}
433
434template <class T>
435constexpr bool operator<=(const Optional<T>& opt, const T& value) {
436 return !(opt > value);
437}
438
439template <class T>
440constexpr bool operator<=(const T& value, const Optional<T>& opt) {
441 return !(value > opt);
442}
443
444template <class T>
445constexpr bool operator>(const Optional<T>& opt, const T& value) {
446 return value < opt;
447}
448
449template <class T>
450constexpr bool operator>(const T& value, const Optional<T>& opt) {
451 return opt < value;
452}
453
454template <class T>
455constexpr bool operator>=(const Optional<T>& opt, const T& value) {
456 return !(opt < value);
457}
458
459template <class T>
460constexpr bool operator>=(const T& value, const Optional<T>& opt) {
461 return !(value < opt);
462}
463
464template <class T>
465constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
466 return Optional<typename std::decay<T>::type>(std::forward<T>(value));
467}
468
469template <class T>
470void swap(Optional<T>& lhs, Optional<T>& rhs) {
471 lhs.swap(rhs);
472}
473
474} // namespace base
475
476namespace std {
477
478template <class T>
479struct hash<base::Optional<T>> {
480 size_t operator()(const base::Optional<T>& opt) const {
481 return opt == base::nullopt ? 0 : std::hash<T>()(*opt);
482 }
483};
484
485} // namespace std
486
487#endif // BASE_OPTIONAL_H_