blob: 518cc71fc1b54d1dc087bfe9068bd1913ea7e4ab [file] [log] [blame]
mlamourid1eac042016-04-20 02:27:01 +09001// 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"
mlamourid1eac042016-04-20 02:27:01 +090011
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
alshabalinc2ac5252016-05-27 17:01:31 +090032namespace internal {
33
danakjd61a1942017-04-06 02:59:27 +090034template <typename T, bool = std::is_trivially_destructible<T>::value>
alshabalinc2ac5252016-05-27 17:01:31 +090035struct OptionalStorage {
alshabalinae9cd1e2016-10-27 07:29:28 +090036 // Initializing |empty_| here instead of using default member initializing
37 // to avoid errors in g++ 4.8.
38 constexpr OptionalStorage() : empty_('\0') {}
alshabalin2085f892016-10-25 18:56:41 +090039
40 constexpr explicit OptionalStorage(const T& value)
41 : is_null_(false), value_(value) {}
42
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +090043 constexpr explicit OptionalStorage(T&& value)
alshabalin2085f892016-10-25 18:56:41 +090044 : is_null_(false), value_(std::move(value)) {}
45
alshabalin2085f892016-10-25 18:56:41 +090046 template <class... Args>
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +090047 constexpr explicit OptionalStorage(base::in_place_t, Args&&... args)
alshabalin2085f892016-10-25 18:56:41 +090048 : is_null_(false), value_(std::forward<Args>(args)...) {}
49
alshabalinc2ac5252016-05-27 17:01:31 +090050 // When T is not trivially destructible we must call its
51 // destructor before deallocating its memory.
52 ~OptionalStorage() {
53 if (!is_null_)
kwiberg55811ed2016-08-16 18:42:21 +090054 value_.~T();
alshabalinc2ac5252016-05-27 17:01:31 +090055 }
56
57 bool is_null_ = true;
kwiberg55811ed2016-08-16 18:42:21 +090058 union {
59 // |empty_| exists so that the union will always be initialized, even when
alshabalinae9cd1e2016-10-27 07:29:28 +090060 // it doesn't contain a value. Union members must be initialized for the
61 // constructor to be 'constexpr'.
62 char empty_;
kwiberg55811ed2016-08-16 18:42:21 +090063 T value_;
64 };
alshabalinc2ac5252016-05-27 17:01:31 +090065};
66
67template <typename T>
68struct OptionalStorage<T, true> {
alshabalinae9cd1e2016-10-27 07:29:28 +090069 // Initializing |empty_| here instead of using default member initializing
70 // to avoid errors in g++ 4.8.
71 constexpr OptionalStorage() : empty_('\0') {}
alshabalin2085f892016-10-25 18:56:41 +090072
73 constexpr explicit OptionalStorage(const T& value)
74 : is_null_(false), value_(value) {}
75
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +090076 constexpr explicit OptionalStorage(T&& value)
alshabalin2085f892016-10-25 18:56:41 +090077 : is_null_(false), value_(std::move(value)) {}
78
alshabalin2085f892016-10-25 18:56:41 +090079 template <class... Args>
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +090080 constexpr explicit OptionalStorage(base::in_place_t, Args&&... args)
alshabalin2085f892016-10-25 18:56:41 +090081 : is_null_(false), value_(std::forward<Args>(args)...) {}
82
kwiberg55811ed2016-08-16 18:42:21 +090083 // 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.
alshabalinc2ac5252016-05-27 17:01:31 +090086 ~OptionalStorage() = default;
87
88 bool is_null_ = true;
kwiberg55811ed2016-08-16 18:42:21 +090089 union {
90 // |empty_| exists so that the union will always be initialized, even when
alshabalinae9cd1e2016-10-27 07:29:28 +090091 // it doesn't contain a value. Union members must be initialized for the
92 // constructor to be 'constexpr'.
93 char empty_;
kwiberg55811ed2016-08-16 18:42:21 +090094 T value_;
95 };
alshabalinc2ac5252016-05-27 17:01:31 +090096};
97
98} // namespace internal
99
mlamourid1eac042016-04-20 02:27:01 +0900100// 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:
alshabalinc2ac5252016-05-27 17:01:31 +0900116 using value_type = T;
117
justincohenad87d2e2016-10-28 11:25:11 +0900118 constexpr Optional() {}
alshabalin2085f892016-10-25 18:56:41 +0900119
120 constexpr Optional(base::nullopt_t) {}
mlamourid1eac042016-04-20 02:27:01 +0900121
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +0900122 // TODO(dcheng): Make these constexpr iff T is trivially constructible.
mlamourid1eac042016-04-20 02:27:01 +0900123 Optional(const Optional& other) {
alshabalinc2ac5252016-05-27 17:01:31 +0900124 if (!other.storage_.is_null_)
mlamourid1eac042016-04-20 02:27:01 +0900125 Init(other.value());
126 }
127
128 Optional(Optional&& other) {
alshabalinc2ac5252016-05-27 17:01:31 +0900129 if (!other.storage_.is_null_)
mlamourid1eac042016-04-20 02:27:01 +0900130 Init(std::move(other.value()));
131 }
132
alshabalin2085f892016-10-25 18:56:41 +0900133 constexpr Optional(const T& value) : storage_(value) {}
mlamourid1eac042016-04-20 02:27:01 +0900134
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +0900135 constexpr Optional(T&& value) : storage_(std::move(value)) {}
mlamourid1eac042016-04-20 02:27:01 +0900136
137 template <class... Args>
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +0900138 constexpr explicit Optional(base::in_place_t, Args&&... args)
alshabalin2085f892016-10-25 18:56:41 +0900139 : storage_(base::in_place, std::forward<Args>(args)...) {}
mlamourid1eac042016-04-20 02:27:01 +0900140
alshabalinc2ac5252016-05-27 17:01:31 +0900141 ~Optional() = default;
mlamourid1eac042016-04-20 02:27:01 +0900142
143 Optional& operator=(base::nullopt_t) {
144 FreeIfNeeded();
145 return *this;
146 }
147
148 Optional& operator=(const Optional& other) {
alshabalinc2ac5252016-05-27 17:01:31 +0900149 if (other.storage_.is_null_) {
mlamourid1eac042016-04-20 02:27:01 +0900150 FreeIfNeeded();
151 return *this;
152 }
153
154 InitOrAssign(other.value());
155 return *this;
156 }
157
158 Optional& operator=(Optional&& other) {
alshabalinc2ac5252016-05-27 17:01:31 +0900159 if (other.storage_.is_null_) {
mlamourid1eac042016-04-20 02:27:01 +0900160 FreeIfNeeded();
161 return *this;
162 }
163
164 InitOrAssign(std::move(other.value()));
165 return *this;
166 }
167
168 template <class U>
Andrey Kraynovc7b46612017-09-14 02:15:44 +0900169 typename std::enable_if<std::is_same<std::decay_t<U>, T>::value,
mlamourid1eac042016-04-20 02:27:01 +0900170 Optional&>::type
171 operator=(U&& value) {
172 InitOrAssign(std::forward<U>(value));
173 return *this;
174 }
175
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +0900176 constexpr const T* operator->() const {
alshabalinc2ac5252016-05-27 17:01:31 +0900177 DCHECK(!storage_.is_null_);
mlamourid1eac042016-04-20 02:27:01 +0900178 return &value();
179 }
180
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +0900181 constexpr T* operator->() {
alshabalinc2ac5252016-05-27 17:01:31 +0900182 DCHECK(!storage_.is_null_);
mlamourid1eac042016-04-20 02:27:01 +0900183 return &value();
184 }
185
186 constexpr const T& operator*() const& { return value(); }
187
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +0900188 constexpr T& operator*() & { return value(); }
mlamourid1eac042016-04-20 02:27:01 +0900189
190 constexpr const T&& operator*() const&& { return std::move(value()); }
191
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +0900192 constexpr T&& operator*() && { return std::move(value()); }
mlamourid1eac042016-04-20 02:27:01 +0900193
alshabalinc2ac5252016-05-27 17:01:31 +0900194 constexpr explicit operator bool() const { return !storage_.is_null_; }
mlamourid1eac042016-04-20 02:27:01 +0900195
mlamouri1b26dd12016-08-10 21:24:15 +0900196 constexpr bool has_value() const { return !storage_.is_null_; }
197
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +0900198 constexpr T& value() & {
alshabalinc2ac5252016-05-27 17:01:31 +0900199 DCHECK(!storage_.is_null_);
kwiberg55811ed2016-08-16 18:42:21 +0900200 return storage_.value_;
mlamourid1eac042016-04-20 02:27:01 +0900201 }
202
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +0900203 constexpr const T& value() const & {
alshabalinc2ac5252016-05-27 17:01:31 +0900204 DCHECK(!storage_.is_null_);
kwiberg55811ed2016-08-16 18:42:21 +0900205 return storage_.value_;
mlamourid1eac042016-04-20 02:27:01 +0900206 }
207
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +0900208 constexpr T&& value() && {
alshabalinc2ac5252016-05-27 17:01:31 +0900209 DCHECK(!storage_.is_null_);
kwiberg55811ed2016-08-16 18:42:21 +0900210 return std::move(storage_.value_);
mlamourid1eac042016-04-20 02:27:01 +0900211 }
212
Daniel Cheng7a7fa9f2017-10-05 00:33:56 +0900213 constexpr const T&& value() const && {
alshabalinc2ac5252016-05-27 17:01:31 +0900214 DCHECK(!storage_.is_null_);
kwiberg55811ed2016-08-16 18:42:21 +0900215 return std::move(storage_.value_);
mlamourid1eac042016-04-20 02:27:01 +0900216 }
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");
alshabalinc2ac5252016-05-27 17:01:31 +0900225 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
226 : value();
mlamourid1eac042016-04-20 02:27:01 +0900227 }
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");
alshabalinc2ac5252016-05-27 17:01:31 +0900236 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
237 : std::move(value());
mlamourid1eac042016-04-20 02:27:01 +0900238 }
239
240 void swap(Optional& other) {
alshabalinc2ac5252016-05-27 17:01:31 +0900241 if (storage_.is_null_ && other.storage_.is_null_)
mlamourid1eac042016-04-20 02:27:01 +0900242 return;
243
alshabalinc2ac5252016-05-27 17:01:31 +0900244 if (storage_.is_null_ != other.storage_.is_null_) {
245 if (storage_.is_null_) {
kwiberg55811ed2016-08-16 18:42:21 +0900246 Init(std::move(other.storage_.value_));
mlamourid1eac042016-04-20 02:27:01 +0900247 other.FreeIfNeeded();
248 } else {
kwiberg55811ed2016-08-16 18:42:21 +0900249 other.Init(std::move(storage_.value_));
mlamourid1eac042016-04-20 02:27:01 +0900250 FreeIfNeeded();
251 }
252 return;
253 }
254
alshabalinc2ac5252016-05-27 17:01:31 +0900255 DCHECK(!storage_.is_null_ && !other.storage_.is_null_);
mlamourid1eac042016-04-20 02:27:01 +0900256 using std::swap;
257 swap(**this, *other);
258 }
259
mlamouri1b26dd12016-08-10 21:24:15 +0900260 void reset() {
261 FreeIfNeeded();
262 }
263
mlamourid1eac042016-04-20 02:27:01 +0900264 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) {
alshabalinc2ac5252016-05-27 17:01:31 +0900272 DCHECK(storage_.is_null_);
kwiberg55811ed2016-08-16 18:42:21 +0900273 new (&storage_.value_) T(value);
alshabalinc2ac5252016-05-27 17:01:31 +0900274 storage_.is_null_ = false;
mlamourid1eac042016-04-20 02:27:01 +0900275 }
276
277 void Init(T&& value) {
alshabalinc2ac5252016-05-27 17:01:31 +0900278 DCHECK(storage_.is_null_);
kwiberg55811ed2016-08-16 18:42:21 +0900279 new (&storage_.value_) T(std::move(value));
alshabalinc2ac5252016-05-27 17:01:31 +0900280 storage_.is_null_ = false;
mlamourid1eac042016-04-20 02:27:01 +0900281 }
282
283 template <class... Args>
284 void Init(Args&&... args) {
alshabalinc2ac5252016-05-27 17:01:31 +0900285 DCHECK(storage_.is_null_);
kwiberg55811ed2016-08-16 18:42:21 +0900286 new (&storage_.value_) T(std::forward<Args>(args)...);
alshabalinc2ac5252016-05-27 17:01:31 +0900287 storage_.is_null_ = false;
mlamourid1eac042016-04-20 02:27:01 +0900288 }
289
290 void InitOrAssign(const T& value) {
alshabalinc2ac5252016-05-27 17:01:31 +0900291 if (storage_.is_null_)
mlamourid1eac042016-04-20 02:27:01 +0900292 Init(value);
293 else
kwiberg55811ed2016-08-16 18:42:21 +0900294 storage_.value_ = value;
mlamourid1eac042016-04-20 02:27:01 +0900295 }
296
297 void InitOrAssign(T&& value) {
alshabalinc2ac5252016-05-27 17:01:31 +0900298 if (storage_.is_null_)
mlamourid1eac042016-04-20 02:27:01 +0900299 Init(std::move(value));
300 else
kwiberg55811ed2016-08-16 18:42:21 +0900301 storage_.value_ = std::move(value);
mlamourid1eac042016-04-20 02:27:01 +0900302 }
303
304 void FreeIfNeeded() {
alshabalinc2ac5252016-05-27 17:01:31 +0900305 if (storage_.is_null_)
mlamourid1eac042016-04-20 02:27:01 +0900306 return;
kwiberg55811ed2016-08-16 18:42:21 +0900307 storage_.value_.~T();
alshabalinc2ac5252016-05-27 17:01:31 +0900308 storage_.is_null_ = true;
mlamourid1eac042016-04-20 02:27:01 +0900309 }
310
alshabalinc2ac5252016-05-27 17:01:31 +0900311 internal::OptionalStorage<T> storage_;
mlamourid1eac042016-04-20 02:27:01 +0900312};
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_