blob: 2be2f821754c7edf5993b9121bf19bef51fff396 [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
43 // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
44 explicit OptionalStorage(T&& value)
45 : is_null_(false), value_(std::move(value)) {}
46
47 // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
48 template <class... Args>
49 explicit OptionalStorage(base::in_place_t, Args&&... args)
50 : is_null_(false), value_(std::forward<Args>(args)...) {}
51
alshabalinf06b07df2016-05-27 08:01:3152 // When T is not trivially destructible we must call its
53 // destructor before deallocating its memory.
54 ~OptionalStorage() {
55 if (!is_null_)
kwiberg882859a2016-08-16 09:42:2156 value_.~T();
alshabalinf06b07df2016-05-27 08:01:3157 }
58
59 bool is_null_ = true;
kwiberg882859a2016-08-16 09:42:2160 union {
61 // |empty_| exists so that the union will always be initialized, even when
alshabalina637f252016-10-26 22:29:2862 // it doesn't contain a value. Union members must be initialized for the
63 // constructor to be 'constexpr'.
64 char empty_;
kwiberg882859a2016-08-16 09:42:2165 T value_;
66 };
alshabalinf06b07df2016-05-27 08:01:3167};
68
69template <typename T>
70struct OptionalStorage<T, true> {
alshabalina637f252016-10-26 22:29:2871 // Initializing |empty_| here instead of using default member initializing
72 // to avoid errors in g++ 4.8.
73 constexpr OptionalStorage() : empty_('\0') {}
alshabalin9494e4542016-10-25 09:56:4174
75 constexpr explicit OptionalStorage(const T& value)
76 : is_null_(false), value_(value) {}
77
78 // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
79 explicit OptionalStorage(T&& value)
80 : is_null_(false), value_(std::move(value)) {}
81
82 // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
83 template <class... Args>
84 explicit OptionalStorage(base::in_place_t, Args&&... args)
85 : is_null_(false), value_(std::forward<Args>(args)...) {}
86
kwiberg882859a2016-08-16 09:42:2187 // When T is trivially destructible (i.e. its destructor does nothing) there
88 // is no need to call it. Explicitly defaulting the destructor means it's not
89 // user-provided. Those two together make this destructor trivial.
alshabalinf06b07df2016-05-27 08:01:3190 ~OptionalStorage() = default;
91
92 bool is_null_ = true;
kwiberg882859a2016-08-16 09:42:2193 union {
94 // |empty_| exists so that the union will always be initialized, even when
alshabalina637f252016-10-26 22:29:2895 // it doesn't contain a value. Union members must be initialized for the
96 // constructor to be 'constexpr'.
97 char empty_;
kwiberg882859a2016-08-16 09:42:2198 T value_;
99 };
alshabalinf06b07df2016-05-27 08:01:31100};
101
102} // namespace internal
103
mlamouri53f6b252016-04-19 17:27:01104// base::Optional is a Chromium version of the C++17 optional class:
105// std::optional documentation:
106// http://en.cppreference.com/w/cpp/utility/optional
107// Chromium documentation:
108// https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
109//
110// These are the differences between the specification and the implementation:
111// - The constructor and emplace method using initializer_list are not
112// implemented because 'initializer_list' is banned from Chromium.
113// - Constructors do not use 'constexpr' as it is a C++14 extension.
114// - 'constexpr' might be missing in some places for reasons specified locally.
115// - No exceptions are thrown, because they are banned from Chromium.
116// - All the non-members are in the 'base' namespace instead of 'std'.
117template <typename T>
118class Optional {
119 public:
alshabalinf06b07df2016-05-27 08:01:31120 using value_type = T;
121
justincohenb869ed92016-10-28 02:25:11122 constexpr Optional() {}
alshabalin9494e4542016-10-25 09:56:41123
124 constexpr Optional(base::nullopt_t) {}
mlamouri53f6b252016-04-19 17:27:01125
126 Optional(const Optional& other) {
alshabalinf06b07df2016-05-27 08:01:31127 if (!other.storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01128 Init(other.value());
129 }
130
131 Optional(Optional&& other) {
alshabalinf06b07df2016-05-27 08:01:31132 if (!other.storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01133 Init(std::move(other.value()));
134 }
135
alshabalin9494e4542016-10-25 09:56:41136 constexpr Optional(const T& value) : storage_(value) {}
mlamouri53f6b252016-04-19 17:27:01137
alshabalin9494e4542016-10-25 09:56:41138 // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
139 Optional(T&& value) : storage_(std::move(value)) {}
mlamouri53f6b252016-04-19 17:27:01140
alshabalin9494e4542016-10-25 09:56:41141 // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
mlamouri53f6b252016-04-19 17:27:01142 template <class... Args>
alshabalin9494e4542016-10-25 09:56:41143 explicit Optional(base::in_place_t, Args&&... args)
144 : storage_(base::in_place, std::forward<Args>(args)...) {}
mlamouri53f6b252016-04-19 17:27:01145
alshabalinf06b07df2016-05-27 08:01:31146 ~Optional() = default;
mlamouri53f6b252016-04-19 17:27:01147
148 Optional& operator=(base::nullopt_t) {
149 FreeIfNeeded();
150 return *this;
151 }
152
153 Optional& operator=(const Optional& other) {
alshabalinf06b07df2016-05-27 08:01:31154 if (other.storage_.is_null_) {
mlamouri53f6b252016-04-19 17:27:01155 FreeIfNeeded();
156 return *this;
157 }
158
159 InitOrAssign(other.value());
160 return *this;
161 }
162
163 Optional& operator=(Optional&& other) {
alshabalinf06b07df2016-05-27 08:01:31164 if (other.storage_.is_null_) {
mlamouri53f6b252016-04-19 17:27:01165 FreeIfNeeded();
166 return *this;
167 }
168
169 InitOrAssign(std::move(other.value()));
170 return *this;
171 }
172
173 template <class U>
174 typename std::enable_if<std::is_same<std::decay<U>, T>::value,
175 Optional&>::type
176 operator=(U&& value) {
177 InitOrAssign(std::forward<U>(value));
178 return *this;
179 }
180
181 // TODO(mlamouri): can't use 'constexpr' with DCHECK.
182 const T* operator->() const {
alshabalinf06b07df2016-05-27 08:01:31183 DCHECK(!storage_.is_null_);
mlamouri53f6b252016-04-19 17:27:01184 return &value();
185 }
186
187 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
188 // meant to be 'constexpr const'.
189 T* operator->() {
alshabalinf06b07df2016-05-27 08:01:31190 DCHECK(!storage_.is_null_);
mlamouri53f6b252016-04-19 17:27:01191 return &value();
192 }
193
194 constexpr const T& operator*() const& { return value(); }
195
196 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
197 // meant to be 'constexpr const'.
198 T& operator*() & { return value(); }
199
200 constexpr const T&& operator*() const&& { return std::move(value()); }
201
202 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
203 // meant to be 'constexpr const'.
204 T&& operator*() && { return std::move(value()); }
205
alshabalinf06b07df2016-05-27 08:01:31206 constexpr explicit operator bool() const { return !storage_.is_null_; }
mlamouri53f6b252016-04-19 17:27:01207
mlamouri26204572016-08-10 12:24:15208 constexpr bool has_value() const { return !storage_.is_null_; }
209
mlamouri53f6b252016-04-19 17:27:01210 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
211 // meant to be 'constexpr const'.
212 T& value() & {
alshabalinf06b07df2016-05-27 08:01:31213 DCHECK(!storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21214 return storage_.value_;
mlamouri53f6b252016-04-19 17:27:01215 }
216
217 // TODO(mlamouri): can't use 'constexpr' with DCHECK.
218 const T& value() const& {
alshabalinf06b07df2016-05-27 08:01:31219 DCHECK(!storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21220 return storage_.value_;
mlamouri53f6b252016-04-19 17:27:01221 }
222
223 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
224 // meant to be 'constexpr const'.
225 T&& value() && {
alshabalinf06b07df2016-05-27 08:01:31226 DCHECK(!storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21227 return std::move(storage_.value_);
mlamouri53f6b252016-04-19 17:27:01228 }
229
230 // TODO(mlamouri): can't use 'constexpr' with DCHECK.
231 const T&& value() const&& {
alshabalinf06b07df2016-05-27 08:01:31232 DCHECK(!storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21233 return std::move(storage_.value_);
mlamouri53f6b252016-04-19 17:27:01234 }
235
236 template <class U>
237 constexpr T value_or(U&& default_value) const& {
238 // TODO(mlamouri): add the following assert when possible:
239 // static_assert(std::is_copy_constructible<T>::value,
240 // "T must be copy constructible");
241 static_assert(std::is_convertible<U, T>::value,
242 "U must be convertible to T");
alshabalinf06b07df2016-05-27 08:01:31243 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
244 : value();
mlamouri53f6b252016-04-19 17:27:01245 }
246
247 template <class U>
248 T value_or(U&& default_value) && {
249 // TODO(mlamouri): add the following assert when possible:
250 // static_assert(std::is_move_constructible<T>::value,
251 // "T must be move constructible");
252 static_assert(std::is_convertible<U, T>::value,
253 "U must be convertible to T");
alshabalinf06b07df2016-05-27 08:01:31254 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
255 : std::move(value());
mlamouri53f6b252016-04-19 17:27:01256 }
257
258 void swap(Optional& other) {
alshabalinf06b07df2016-05-27 08:01:31259 if (storage_.is_null_ && other.storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01260 return;
261
alshabalinf06b07df2016-05-27 08:01:31262 if (storage_.is_null_ != other.storage_.is_null_) {
263 if (storage_.is_null_) {
kwiberg882859a2016-08-16 09:42:21264 Init(std::move(other.storage_.value_));
mlamouri53f6b252016-04-19 17:27:01265 other.FreeIfNeeded();
266 } else {
kwiberg882859a2016-08-16 09:42:21267 other.Init(std::move(storage_.value_));
mlamouri53f6b252016-04-19 17:27:01268 FreeIfNeeded();
269 }
270 return;
271 }
272
alshabalinf06b07df2016-05-27 08:01:31273 DCHECK(!storage_.is_null_ && !other.storage_.is_null_);
mlamouri53f6b252016-04-19 17:27:01274 using std::swap;
275 swap(**this, *other);
276 }
277
mlamouri26204572016-08-10 12:24:15278 void reset() {
279 FreeIfNeeded();
280 }
281
mlamouri53f6b252016-04-19 17:27:01282 template <class... Args>
283 void emplace(Args&&... args) {
284 FreeIfNeeded();
285 Init(std::forward<Args>(args)...);
286 }
287
288 private:
289 void Init(const T& value) {
alshabalinf06b07df2016-05-27 08:01:31290 DCHECK(storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21291 new (&storage_.value_) T(value);
alshabalinf06b07df2016-05-27 08:01:31292 storage_.is_null_ = false;
mlamouri53f6b252016-04-19 17:27:01293 }
294
295 void Init(T&& value) {
alshabalinf06b07df2016-05-27 08:01:31296 DCHECK(storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21297 new (&storage_.value_) T(std::move(value));
alshabalinf06b07df2016-05-27 08:01:31298 storage_.is_null_ = false;
mlamouri53f6b252016-04-19 17:27:01299 }
300
301 template <class... Args>
302 void Init(Args&&... args) {
alshabalinf06b07df2016-05-27 08:01:31303 DCHECK(storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21304 new (&storage_.value_) T(std::forward<Args>(args)...);
alshabalinf06b07df2016-05-27 08:01:31305 storage_.is_null_ = false;
mlamouri53f6b252016-04-19 17:27:01306 }
307
308 void InitOrAssign(const T& value) {
alshabalinf06b07df2016-05-27 08:01:31309 if (storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01310 Init(value);
311 else
kwiberg882859a2016-08-16 09:42:21312 storage_.value_ = value;
mlamouri53f6b252016-04-19 17:27:01313 }
314
315 void InitOrAssign(T&& value) {
alshabalinf06b07df2016-05-27 08:01:31316 if (storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01317 Init(std::move(value));
318 else
kwiberg882859a2016-08-16 09:42:21319 storage_.value_ = std::move(value);
mlamouri53f6b252016-04-19 17:27:01320 }
321
322 void FreeIfNeeded() {
alshabalinf06b07df2016-05-27 08:01:31323 if (storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01324 return;
kwiberg882859a2016-08-16 09:42:21325 storage_.value_.~T();
alshabalinf06b07df2016-05-27 08:01:31326 storage_.is_null_ = true;
mlamouri53f6b252016-04-19 17:27:01327 }
328
alshabalinf06b07df2016-05-27 08:01:31329 internal::OptionalStorage<T> storage_;
mlamouri53f6b252016-04-19 17:27:01330};
331
332template <class T>
333constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
334 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
335}
336
337template <class T>
338constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) {
339 return !(lhs == rhs);
340}
341
342template <class T>
343constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) {
344 return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs);
345}
346
347template <class T>
348constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) {
349 return !(rhs < lhs);
350}
351
352template <class T>
353constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) {
354 return rhs < lhs;
355}
356
357template <class T>
358constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) {
359 return !(lhs < rhs);
360}
361
362template <class T>
363constexpr bool operator==(const Optional<T>& opt, base::nullopt_t) {
364 return !opt;
365}
366
367template <class T>
368constexpr bool operator==(base::nullopt_t, const Optional<T>& opt) {
369 return !opt;
370}
371
372template <class T>
373constexpr bool operator!=(const Optional<T>& opt, base::nullopt_t) {
374 return !!opt;
375}
376
377template <class T>
378constexpr bool operator!=(base::nullopt_t, const Optional<T>& opt) {
379 return !!opt;
380}
381
382template <class T>
383constexpr bool operator<(const Optional<T>& opt, base::nullopt_t) {
384 return false;
385}
386
387template <class T>
388constexpr bool operator<(base::nullopt_t, const Optional<T>& opt) {
389 return !!opt;
390}
391
392template <class T>
393constexpr bool operator<=(const Optional<T>& opt, base::nullopt_t) {
394 return !opt;
395}
396
397template <class T>
398constexpr bool operator<=(base::nullopt_t, const Optional<T>& opt) {
399 return true;
400}
401
402template <class T>
403constexpr bool operator>(const Optional<T>& opt, base::nullopt_t) {
404 return !!opt;
405}
406
407template <class T>
408constexpr bool operator>(base::nullopt_t, const Optional<T>& opt) {
409 return false;
410}
411
412template <class T>
413constexpr bool operator>=(const Optional<T>& opt, base::nullopt_t) {
414 return true;
415}
416
417template <class T>
418constexpr bool operator>=(base::nullopt_t, const Optional<T>& opt) {
419 return !opt;
420}
421
422template <class T>
423constexpr bool operator==(const Optional<T>& opt, const T& value) {
424 return opt != nullopt ? *opt == value : false;
425}
426
427template <class T>
428constexpr bool operator==(const T& value, const Optional<T>& opt) {
429 return opt == value;
430}
431
432template <class T>
433constexpr bool operator!=(const Optional<T>& opt, const T& value) {
434 return !(opt == value);
435}
436
437template <class T>
438constexpr bool operator!=(const T& value, const Optional<T>& opt) {
439 return !(opt == value);
440}
441
442template <class T>
443constexpr bool operator<(const Optional<T>& opt, const T& value) {
444 return opt != nullopt ? *opt < value : true;
445}
446
447template <class T>
448constexpr bool operator<(const T& value, const Optional<T>& opt) {
449 return opt != nullopt ? value < *opt : false;
450}
451
452template <class T>
453constexpr bool operator<=(const Optional<T>& opt, const T& value) {
454 return !(opt > value);
455}
456
457template <class T>
458constexpr bool operator<=(const T& value, const Optional<T>& opt) {
459 return !(value > opt);
460}
461
462template <class T>
463constexpr bool operator>(const Optional<T>& opt, const T& value) {
464 return value < opt;
465}
466
467template <class T>
468constexpr bool operator>(const T& value, const Optional<T>& opt) {
469 return opt < value;
470}
471
472template <class T>
473constexpr bool operator>=(const Optional<T>& opt, const T& value) {
474 return !(opt < value);
475}
476
477template <class T>
478constexpr bool operator>=(const T& value, const Optional<T>& opt) {
479 return !(value < opt);
480}
481
482template <class T>
483constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
484 return Optional<typename std::decay<T>::type>(std::forward<T>(value));
485}
486
487template <class T>
488void swap(Optional<T>& lhs, Optional<T>& rhs) {
489 lhs.swap(rhs);
490}
491
492} // namespace base
493
494namespace std {
495
496template <class T>
497struct hash<base::Optional<T>> {
498 size_t operator()(const base::Optional<T>& opt) const {
499 return opt == base::nullopt ? 0 : std::hash<T>()(*opt);
500 }
501};
502
503} // namespace std
504
505#endif // BASE_OPTIONAL_H_