blob: a32e06808d82cd7b75b66009880d891e4d62fceb [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"
alshabalinc2ac5252016-05-27 17:01:31 +090011#include "base/template_util.h"
mlamourid1eac042016-04-20 02:27:01 +090012
13namespace base {
14
15// Specification:
16// http://en.cppreference.com/w/cpp/utility/optional/in_place_t
17struct in_place_t {};
18
19// Specification:
20// http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
21struct nullopt_t {
22 constexpr explicit nullopt_t(int) {}
23};
24
25// Specification:
26// http://en.cppreference.com/w/cpp/utility/optional/in_place
27constexpr in_place_t in_place = {};
28
29// Specification:
30// http://en.cppreference.com/w/cpp/utility/optional/nullopt
31constexpr nullopt_t nullopt(0);
32
alshabalinc2ac5252016-05-27 17:01:31 +090033namespace internal {
34
35template <typename T, bool = base::is_trivially_destructible<T>::value>
36struct OptionalStorage {
alshabalin2085f892016-10-25 18:56:41 +090037 constexpr OptionalStorage() {}
38
39 constexpr explicit OptionalStorage(const T& value)
40 : is_null_(false), value_(value) {}
41
42 // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
43 explicit OptionalStorage(T&& value)
44 : is_null_(false), value_(std::move(value)) {}
45
46 // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
47 template <class... Args>
48 explicit OptionalStorage(base::in_place_t, Args&&... args)
49 : is_null_(false), value_(std::forward<Args>(args)...) {}
50
alshabalinc2ac5252016-05-27 17:01:31 +090051 // When T is not trivially destructible we must call its
52 // destructor before deallocating its memory.
53 ~OptionalStorage() {
54 if (!is_null_)
kwiberg55811ed2016-08-16 18:42:21 +090055 value_.~T();
alshabalinc2ac5252016-05-27 17:01:31 +090056 }
57
58 bool is_null_ = true;
kwiberg55811ed2016-08-16 18:42:21 +090059 union {
60 // |empty_| exists so that the union will always be initialized, even when
61 // it doesn't contain a value. Not initializing it has been observed to
62 // trigger comiler warnings.
63 char empty_ = '\0';
64 T value_;
65 };
alshabalinc2ac5252016-05-27 17:01:31 +090066};
67
68template <typename T>
69struct OptionalStorage<T, true> {
alshabalin2085f892016-10-25 18:56:41 +090070 constexpr OptionalStorage() {}
71
72 constexpr explicit OptionalStorage(const T& value)
73 : is_null_(false), value_(value) {}
74
75 // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
76 explicit OptionalStorage(T&& value)
77 : is_null_(false), value_(std::move(value)) {}
78
79 // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
80 template <class... Args>
81 explicit OptionalStorage(base::in_place_t, Args&&... args)
82 : is_null_(false), value_(std::forward<Args>(args)...) {}
83
kwiberg55811ed2016-08-16 18:42:21 +090084 // When T is trivially destructible (i.e. its destructor does nothing) there
85 // is no need to call it. Explicitly defaulting the destructor means it's not
86 // user-provided. Those two together make this destructor trivial.
alshabalinc2ac5252016-05-27 17:01:31 +090087 ~OptionalStorage() = default;
88
89 bool is_null_ = true;
kwiberg55811ed2016-08-16 18:42:21 +090090 union {
91 // |empty_| exists so that the union will always be initialized, even when
92 // it doesn't contain a value. Not initializing it has been observed to
93 // trigger comiler warnings.
94 char empty_ = '\0';
95 T value_;
96 };
alshabalinc2ac5252016-05-27 17:01:31 +090097};
98
99} // namespace internal
100
mlamourid1eac042016-04-20 02:27:01 +0900101// base::Optional is a Chromium version of the C++17 optional class:
102// std::optional documentation:
103// http://en.cppreference.com/w/cpp/utility/optional
104// Chromium documentation:
105// https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
106//
107// These are the differences between the specification and the implementation:
108// - The constructor and emplace method using initializer_list are not
109// implemented because 'initializer_list' is banned from Chromium.
110// - Constructors do not use 'constexpr' as it is a C++14 extension.
111// - 'constexpr' might be missing in some places for reasons specified locally.
112// - No exceptions are thrown, because they are banned from Chromium.
113// - All the non-members are in the 'base' namespace instead of 'std'.
114template <typename T>
115class Optional {
116 public:
alshabalinc2ac5252016-05-27 17:01:31 +0900117 using value_type = T;
118
mlamourid1eac042016-04-20 02:27:01 +0900119 constexpr Optional() = default;
alshabalin2085f892016-10-25 18:56:41 +0900120
121 constexpr Optional(base::nullopt_t) {}
mlamourid1eac042016-04-20 02:27:01 +0900122
123 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
alshabalin2085f892016-10-25 18:56:41 +0900135 // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
136 Optional(T&& value) : storage_(std::move(value)) {}
mlamourid1eac042016-04-20 02:27:01 +0900137
alshabalin2085f892016-10-25 18:56:41 +0900138 // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
mlamourid1eac042016-04-20 02:27:01 +0900139 template <class... Args>
alshabalin2085f892016-10-25 18:56:41 +0900140 explicit Optional(base::in_place_t, Args&&... args)
141 : storage_(base::in_place, std::forward<Args>(args)...) {}
mlamourid1eac042016-04-20 02:27:01 +0900142
alshabalinc2ac5252016-05-27 17:01:31 +0900143 ~Optional() = default;
mlamourid1eac042016-04-20 02:27:01 +0900144
145 Optional& operator=(base::nullopt_t) {
146 FreeIfNeeded();
147 return *this;
148 }
149
150 Optional& operator=(const Optional& other) {
alshabalinc2ac5252016-05-27 17:01:31 +0900151 if (other.storage_.is_null_) {
mlamourid1eac042016-04-20 02:27:01 +0900152 FreeIfNeeded();
153 return *this;
154 }
155
156 InitOrAssign(other.value());
157 return *this;
158 }
159
160 Optional& operator=(Optional&& other) {
alshabalinc2ac5252016-05-27 17:01:31 +0900161 if (other.storage_.is_null_) {
mlamourid1eac042016-04-20 02:27:01 +0900162 FreeIfNeeded();
163 return *this;
164 }
165
166 InitOrAssign(std::move(other.value()));
167 return *this;
168 }
169
170 template <class U>
171 typename std::enable_if<std::is_same<std::decay<U>, T>::value,
172 Optional&>::type
173 operator=(U&& value) {
174 InitOrAssign(std::forward<U>(value));
175 return *this;
176 }
177
178 // TODO(mlamouri): can't use 'constexpr' with DCHECK.
179 const T* operator->() const {
alshabalinc2ac5252016-05-27 17:01:31 +0900180 DCHECK(!storage_.is_null_);
mlamourid1eac042016-04-20 02:27:01 +0900181 return &value();
182 }
183
184 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
185 // meant to be 'constexpr const'.
186 T* operator->() {
alshabalinc2ac5252016-05-27 17:01:31 +0900187 DCHECK(!storage_.is_null_);
mlamourid1eac042016-04-20 02:27:01 +0900188 return &value();
189 }
190
191 constexpr const T& operator*() const& { return value(); }
192
193 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
194 // meant to be 'constexpr const'.
195 T& operator*() & { return value(); }
196
197 constexpr const T&& operator*() const&& { return std::move(value()); }
198
199 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
200 // meant to be 'constexpr const'.
201 T&& operator*() && { return std::move(value()); }
202
alshabalinc2ac5252016-05-27 17:01:31 +0900203 constexpr explicit operator bool() const { return !storage_.is_null_; }
mlamourid1eac042016-04-20 02:27:01 +0900204
mlamouri1b26dd12016-08-10 21:24:15 +0900205 constexpr bool has_value() const { return !storage_.is_null_; }
206
mlamourid1eac042016-04-20 02:27:01 +0900207 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
208 // meant to be 'constexpr const'.
209 T& value() & {
alshabalinc2ac5252016-05-27 17:01:31 +0900210 DCHECK(!storage_.is_null_);
kwiberg55811ed2016-08-16 18:42:21 +0900211 return storage_.value_;
mlamourid1eac042016-04-20 02:27:01 +0900212 }
213
214 // TODO(mlamouri): can't use 'constexpr' with DCHECK.
215 const T& value() const& {
alshabalinc2ac5252016-05-27 17:01:31 +0900216 DCHECK(!storage_.is_null_);
kwiberg55811ed2016-08-16 18:42:21 +0900217 return storage_.value_;
mlamourid1eac042016-04-20 02:27:01 +0900218 }
219
220 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
221 // meant to be 'constexpr const'.
222 T&& value() && {
alshabalinc2ac5252016-05-27 17:01:31 +0900223 DCHECK(!storage_.is_null_);
kwiberg55811ed2016-08-16 18:42:21 +0900224 return std::move(storage_.value_);
mlamourid1eac042016-04-20 02:27:01 +0900225 }
226
227 // TODO(mlamouri): can't use 'constexpr' with DCHECK.
228 const T&& value() const&& {
alshabalinc2ac5252016-05-27 17:01:31 +0900229 DCHECK(!storage_.is_null_);
kwiberg55811ed2016-08-16 18:42:21 +0900230 return std::move(storage_.value_);
mlamourid1eac042016-04-20 02:27:01 +0900231 }
232
233 template <class U>
234 constexpr T value_or(U&& default_value) const& {
235 // TODO(mlamouri): add the following assert when possible:
236 // static_assert(std::is_copy_constructible<T>::value,
237 // "T must be copy constructible");
238 static_assert(std::is_convertible<U, T>::value,
239 "U must be convertible to T");
alshabalinc2ac5252016-05-27 17:01:31 +0900240 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
241 : value();
mlamourid1eac042016-04-20 02:27:01 +0900242 }
243
244 template <class U>
245 T value_or(U&& default_value) && {
246 // TODO(mlamouri): add the following assert when possible:
247 // static_assert(std::is_move_constructible<T>::value,
248 // "T must be move constructible");
249 static_assert(std::is_convertible<U, T>::value,
250 "U must be convertible to T");
alshabalinc2ac5252016-05-27 17:01:31 +0900251 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
252 : std::move(value());
mlamourid1eac042016-04-20 02:27:01 +0900253 }
254
255 void swap(Optional& other) {
alshabalinc2ac5252016-05-27 17:01:31 +0900256 if (storage_.is_null_ && other.storage_.is_null_)
mlamourid1eac042016-04-20 02:27:01 +0900257 return;
258
alshabalinc2ac5252016-05-27 17:01:31 +0900259 if (storage_.is_null_ != other.storage_.is_null_) {
260 if (storage_.is_null_) {
kwiberg55811ed2016-08-16 18:42:21 +0900261 Init(std::move(other.storage_.value_));
mlamourid1eac042016-04-20 02:27:01 +0900262 other.FreeIfNeeded();
263 } else {
kwiberg55811ed2016-08-16 18:42:21 +0900264 other.Init(std::move(storage_.value_));
mlamourid1eac042016-04-20 02:27:01 +0900265 FreeIfNeeded();
266 }
267 return;
268 }
269
alshabalinc2ac5252016-05-27 17:01:31 +0900270 DCHECK(!storage_.is_null_ && !other.storage_.is_null_);
mlamourid1eac042016-04-20 02:27:01 +0900271 using std::swap;
272 swap(**this, *other);
273 }
274
mlamouri1b26dd12016-08-10 21:24:15 +0900275 void reset() {
276 FreeIfNeeded();
277 }
278
mlamourid1eac042016-04-20 02:27:01 +0900279 template <class... Args>
280 void emplace(Args&&... args) {
281 FreeIfNeeded();
282 Init(std::forward<Args>(args)...);
283 }
284
285 private:
286 void Init(const T& value) {
alshabalinc2ac5252016-05-27 17:01:31 +0900287 DCHECK(storage_.is_null_);
kwiberg55811ed2016-08-16 18:42:21 +0900288 new (&storage_.value_) T(value);
alshabalinc2ac5252016-05-27 17:01:31 +0900289 storage_.is_null_ = false;
mlamourid1eac042016-04-20 02:27:01 +0900290 }
291
292 void Init(T&& value) {
alshabalinc2ac5252016-05-27 17:01:31 +0900293 DCHECK(storage_.is_null_);
kwiberg55811ed2016-08-16 18:42:21 +0900294 new (&storage_.value_) T(std::move(value));
alshabalinc2ac5252016-05-27 17:01:31 +0900295 storage_.is_null_ = false;
mlamourid1eac042016-04-20 02:27:01 +0900296 }
297
298 template <class... Args>
299 void Init(Args&&... args) {
alshabalinc2ac5252016-05-27 17:01:31 +0900300 DCHECK(storage_.is_null_);
kwiberg55811ed2016-08-16 18:42:21 +0900301 new (&storage_.value_) T(std::forward<Args>(args)...);
alshabalinc2ac5252016-05-27 17:01:31 +0900302 storage_.is_null_ = false;
mlamourid1eac042016-04-20 02:27:01 +0900303 }
304
305 void InitOrAssign(const T& value) {
alshabalinc2ac5252016-05-27 17:01:31 +0900306 if (storage_.is_null_)
mlamourid1eac042016-04-20 02:27:01 +0900307 Init(value);
308 else
kwiberg55811ed2016-08-16 18:42:21 +0900309 storage_.value_ = value;
mlamourid1eac042016-04-20 02:27:01 +0900310 }
311
312 void InitOrAssign(T&& value) {
alshabalinc2ac5252016-05-27 17:01:31 +0900313 if (storage_.is_null_)
mlamourid1eac042016-04-20 02:27:01 +0900314 Init(std::move(value));
315 else
kwiberg55811ed2016-08-16 18:42:21 +0900316 storage_.value_ = std::move(value);
mlamourid1eac042016-04-20 02:27:01 +0900317 }
318
319 void FreeIfNeeded() {
alshabalinc2ac5252016-05-27 17:01:31 +0900320 if (storage_.is_null_)
mlamourid1eac042016-04-20 02:27:01 +0900321 return;
kwiberg55811ed2016-08-16 18:42:21 +0900322 storage_.value_.~T();
alshabalinc2ac5252016-05-27 17:01:31 +0900323 storage_.is_null_ = true;
mlamourid1eac042016-04-20 02:27:01 +0900324 }
325
alshabalinc2ac5252016-05-27 17:01:31 +0900326 internal::OptionalStorage<T> storage_;
mlamourid1eac042016-04-20 02:27:01 +0900327};
328
329template <class T>
330constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
331 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
332}
333
334template <class T>
335constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) {
336 return !(lhs == rhs);
337}
338
339template <class T>
340constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) {
341 return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs);
342}
343
344template <class T>
345constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) {
346 return !(rhs < lhs);
347}
348
349template <class T>
350constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) {
351 return rhs < lhs;
352}
353
354template <class T>
355constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) {
356 return !(lhs < rhs);
357}
358
359template <class T>
360constexpr bool operator==(const Optional<T>& opt, base::nullopt_t) {
361 return !opt;
362}
363
364template <class T>
365constexpr bool operator==(base::nullopt_t, const Optional<T>& opt) {
366 return !opt;
367}
368
369template <class T>
370constexpr bool operator!=(const Optional<T>& opt, base::nullopt_t) {
371 return !!opt;
372}
373
374template <class T>
375constexpr bool operator!=(base::nullopt_t, const Optional<T>& opt) {
376 return !!opt;
377}
378
379template <class T>
380constexpr bool operator<(const Optional<T>& opt, base::nullopt_t) {
381 return false;
382}
383
384template <class T>
385constexpr bool operator<(base::nullopt_t, const Optional<T>& opt) {
386 return !!opt;
387}
388
389template <class T>
390constexpr bool operator<=(const Optional<T>& opt, base::nullopt_t) {
391 return !opt;
392}
393
394template <class T>
395constexpr bool operator<=(base::nullopt_t, const Optional<T>& opt) {
396 return true;
397}
398
399template <class T>
400constexpr bool operator>(const Optional<T>& opt, base::nullopt_t) {
401 return !!opt;
402}
403
404template <class T>
405constexpr bool operator>(base::nullopt_t, const Optional<T>& opt) {
406 return false;
407}
408
409template <class T>
410constexpr bool operator>=(const Optional<T>& opt, base::nullopt_t) {
411 return true;
412}
413
414template <class T>
415constexpr bool operator>=(base::nullopt_t, const Optional<T>& opt) {
416 return !opt;
417}
418
419template <class T>
420constexpr bool operator==(const Optional<T>& opt, const T& value) {
421 return opt != nullopt ? *opt == value : false;
422}
423
424template <class T>
425constexpr bool operator==(const T& value, const Optional<T>& opt) {
426 return opt == value;
427}
428
429template <class T>
430constexpr bool operator!=(const Optional<T>& opt, const T& value) {
431 return !(opt == value);
432}
433
434template <class T>
435constexpr bool operator!=(const T& value, const Optional<T>& opt) {
436 return !(opt == value);
437}
438
439template <class T>
440constexpr bool operator<(const Optional<T>& opt, const T& value) {
441 return opt != nullopt ? *opt < value : true;
442}
443
444template <class T>
445constexpr bool operator<(const T& value, const Optional<T>& opt) {
446 return opt != nullopt ? value < *opt : false;
447}
448
449template <class T>
450constexpr bool operator<=(const Optional<T>& opt, const T& value) {
451 return !(opt > value);
452}
453
454template <class T>
455constexpr bool operator<=(const T& value, const Optional<T>& opt) {
456 return !(value > opt);
457}
458
459template <class T>
460constexpr bool operator>(const Optional<T>& opt, const T& value) {
461 return value < opt;
462}
463
464template <class T>
465constexpr bool operator>(const T& value, const Optional<T>& opt) {
466 return opt < value;
467}
468
469template <class T>
470constexpr bool operator>=(const Optional<T>& opt, const T& value) {
471 return !(opt < value);
472}
473
474template <class T>
475constexpr bool operator>=(const T& value, const Optional<T>& opt) {
476 return !(value < opt);
477}
478
479template <class T>
480constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
481 return Optional<typename std::decay<T>::type>(std::forward<T>(value));
482}
483
484template <class T>
485void swap(Optional<T>& lhs, Optional<T>& rhs) {
486 lhs.swap(rhs);
487}
488
489} // namespace base
490
491namespace std {
492
493template <class T>
494struct hash<base::Optional<T>> {
495 size_t operator()(const base::Optional<T>& opt) const {
496 return opt == base::nullopt ? 0 : std::hash<T>()(*opt);
497 }
498};
499
500} // namespace std
501
502#endif // BASE_OPTIONAL_H_