blob: a97cc6d8a4acf896aaaf443c6db443227ed90beb [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>
Hidehiko Abe25bbd5e2017-12-20 04:43:079#include <utility>
mlamouri53f6b252016-04-19 17:27:0110
11#include "base/logging.h"
mlamouri53f6b252016-04-19 17:27:0112
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
alshabalinf06b07df2016-05-27 08:01:3133namespace internal {
34
danakj90acaf8292017-04-05 17:59:2735template <typename T, bool = std::is_trivially_destructible<T>::value>
alshabalinf06b07df2016-05-27 08:01:3136struct OptionalStorage {
alshabalina637f252016-10-26 22:29:2837 // Initializing |empty_| here instead of using default member initializing
38 // to avoid errors in g++ 4.8.
39 constexpr OptionalStorage() : empty_('\0') {}
alshabalin9494e4542016-10-25 09:56:4140
alshabalin9494e4542016-10-25 09:56:4141 template <class... Args>
Hidehiko Abe25bbd5e2017-12-20 04:43:0742 constexpr explicit OptionalStorage(in_place_t, Args&&... args)
alshabalin9494e4542016-10-25 09:56:4143 : is_null_(false), value_(std::forward<Args>(args)...) {}
44
alshabalinf06b07df2016-05-27 08:01:3145 // When T is not trivially destructible we must call its
46 // destructor before deallocating its memory.
47 ~OptionalStorage() {
48 if (!is_null_)
kwiberg882859a2016-08-16 09:42:2149 value_.~T();
alshabalinf06b07df2016-05-27 08:01:3150 }
51
52 bool is_null_ = true;
kwiberg882859a2016-08-16 09:42:2153 union {
54 // |empty_| exists so that the union will always be initialized, even when
alshabalina637f252016-10-26 22:29:2855 // it doesn't contain a value. Union members must be initialized for the
56 // constructor to be 'constexpr'.
57 char empty_;
kwiberg882859a2016-08-16 09:42:2158 T value_;
59 };
alshabalinf06b07df2016-05-27 08:01:3160};
61
62template <typename T>
63struct OptionalStorage<T, true> {
alshabalina637f252016-10-26 22:29:2864 // Initializing |empty_| here instead of using default member initializing
65 // to avoid errors in g++ 4.8.
66 constexpr OptionalStorage() : empty_('\0') {}
alshabalin9494e4542016-10-25 09:56:4167
alshabalin9494e4542016-10-25 09:56:4168 template <class... Args>
Hidehiko Abe25bbd5e2017-12-20 04:43:0769 constexpr explicit OptionalStorage(in_place_t, Args&&... args)
alshabalin9494e4542016-10-25 09:56:4170 : is_null_(false), value_(std::forward<Args>(args)...) {}
71
kwiberg882859a2016-08-16 09:42:2172 // When T is trivially destructible (i.e. its destructor does nothing) there
73 // is no need to call it. Explicitly defaulting the destructor means it's not
74 // user-provided. Those two together make this destructor trivial.
alshabalinf06b07df2016-05-27 08:01:3175 ~OptionalStorage() = default;
76
77 bool is_null_ = true;
kwiberg882859a2016-08-16 09:42:2178 union {
79 // |empty_| exists so that the union will always be initialized, even when
alshabalina637f252016-10-26 22:29:2880 // it doesn't contain a value. Union members must be initialized for the
81 // constructor to be 'constexpr'.
82 char empty_;
kwiberg882859a2016-08-16 09:42:2183 T value_;
84 };
alshabalinf06b07df2016-05-27 08:01:3185};
86
Hidehiko Abe25bbd5e2017-12-20 04:43:0787// Base class to support conditionally usable copy-/move- constructors
88// and assign operators.
89template <typename T>
90class OptionalBase {
91 // This class provides implementation rather than public API, so everything
92 // should be hidden. Often we use composition, but we cannot in this case
93 // because of C++ language restriction.
94 protected:
95 constexpr OptionalBase() = default;
96
97 // TODO(dcheng): Make these constexpr iff T is trivially constructible.
98 OptionalBase(const OptionalBase& other) {
99 if (!other.storage_.is_null_)
100 Init(other.storage_.value_);
101 }
102
103 OptionalBase(OptionalBase&& other) {
104 if (!other.storage_.is_null_)
105 Init(std::move(other.storage_.value_));
106 }
107
108 template <class... Args>
109 constexpr explicit OptionalBase(in_place_t, Args&&... args)
110 : storage_(in_place, std::forward<Args>(args)...) {}
111
112 ~OptionalBase() = default;
113
114 OptionalBase& operator=(const OptionalBase& other) {
115 if (other.storage_.is_null_) {
116 FreeIfNeeded();
117 return *this;
118 }
119
120 InitOrAssign(other.storage_.value_);
121 return *this;
122 }
123
124 OptionalBase& operator=(OptionalBase&& other) {
125 if (other.storage_.is_null_) {
126 FreeIfNeeded();
127 return *this;
128 }
129
130 InitOrAssign(std::move(other.storage_.value_));
131 return *this;
132 }
133
134 template <class... Args>
135 void Init(Args&&... args) {
136 DCHECK(storage_.is_null_);
137 new (&storage_.value_) T(std::forward<Args>(args)...);
138 storage_.is_null_ = false;
139 }
140
141 void InitOrAssign(const T& value) {
142 if (storage_.is_null_)
143 Init(value);
144 else
145 storage_.value_ = value;
146 }
147
148 void InitOrAssign(T&& value) {
149 if (storage_.is_null_)
150 Init(std::move(value));
151 else
152 storage_.value_ = std::move(value);
153 }
154
155 void FreeIfNeeded() {
156 if (storage_.is_null_)
157 return;
158 storage_.value_.~T();
159 storage_.is_null_ = true;
160 }
161
162 OptionalStorage<T> storage_;
163};
164
alshabalinf06b07df2016-05-27 08:01:31165} // namespace internal
166
mlamouri53f6b252016-04-19 17:27:01167// base::Optional is a Chromium version of the C++17 optional class:
168// std::optional documentation:
169// http://en.cppreference.com/w/cpp/utility/optional
170// Chromium documentation:
171// https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
172//
173// These are the differences between the specification and the implementation:
174// - The constructor and emplace method using initializer_list are not
175// implemented because 'initializer_list' is banned from Chromium.
176// - Constructors do not use 'constexpr' as it is a C++14 extension.
177// - 'constexpr' might be missing in some places for reasons specified locally.
178// - No exceptions are thrown, because they are banned from Chromium.
179// - All the non-members are in the 'base' namespace instead of 'std'.
180template <typename T>
Hidehiko Abe25bbd5e2017-12-20 04:43:07181class Optional : public internal::OptionalBase<T> {
mlamouri53f6b252016-04-19 17:27:01182 public:
alshabalinf06b07df2016-05-27 08:01:31183 using value_type = T;
184
Hidehiko Abe25bbd5e2017-12-20 04:43:07185 // Defer default/copy/move constructor implementation to OptionalBase.
186 // TODO(hidehiko): Implement conditional enabling.
Chris Watkins091d6292017-12-13 04:25:58187 constexpr Optional() = default;
Hidehiko Abe25bbd5e2017-12-20 04:43:07188 Optional(const Optional& other) = default;
189 Optional(Optional&& other) = default;
alshabalin9494e4542016-10-25 09:56:41190
Hidehiko Abe25bbd5e2017-12-20 04:43:07191 constexpr Optional(nullopt_t) {}
mlamouri53f6b252016-04-19 17:27:01192
Hidehiko Abe25bbd5e2017-12-20 04:43:07193 constexpr Optional(const T& value)
194 : internal::OptionalBase<T>(in_place, value) {}
mlamouri53f6b252016-04-19 17:27:01195
Hidehiko Abe25bbd5e2017-12-20 04:43:07196 constexpr Optional(T&& value)
197 : internal::OptionalBase<T>(in_place, std::move(value)) {}
mlamouri53f6b252016-04-19 17:27:01198
199 template <class... Args>
Hidehiko Abe25bbd5e2017-12-20 04:43:07200 constexpr explicit Optional(in_place_t, Args&&... args)
201 : internal::OptionalBase<T>(in_place, std::forward<Args>(args)...) {}
mlamouri53f6b252016-04-19 17:27:01202
alshabalinf06b07df2016-05-27 08:01:31203 ~Optional() = default;
mlamouri53f6b252016-04-19 17:27:01204
Hidehiko Abe25bbd5e2017-12-20 04:43:07205 // Defer copy-/move- assign operator implementation to OptionalBase.
206 // TOOD(hidehiko): Implement conditional enabling.
207 Optional& operator=(const Optional& other) = default;
208 Optional& operator=(Optional&& other) = default;
209
210 Optional& operator=(nullopt_t) {
mlamouri53f6b252016-04-19 17:27:01211 FreeIfNeeded();
212 return *this;
213 }
214
mlamouri53f6b252016-04-19 17:27:01215 template <class U>
Andrey Kraynov976fbbd2017-09-13 17:15:44216 typename std::enable_if<std::is_same<std::decay_t<U>, T>::value,
mlamouri53f6b252016-04-19 17:27:01217 Optional&>::type
218 operator=(U&& value) {
219 InitOrAssign(std::forward<U>(value));
220 return *this;
221 }
222
Daniel Cheng429dbdc52017-10-04 15:33:56223 constexpr const T* operator->() const {
alshabalinf06b07df2016-05-27 08:01:31224 DCHECK(!storage_.is_null_);
mlamouri53f6b252016-04-19 17:27:01225 return &value();
226 }
227
Daniel Cheng429dbdc52017-10-04 15:33:56228 constexpr T* operator->() {
alshabalinf06b07df2016-05-27 08:01:31229 DCHECK(!storage_.is_null_);
mlamouri53f6b252016-04-19 17:27:01230 return &value();
231 }
232
233 constexpr const T& operator*() const& { return value(); }
234
Daniel Cheng429dbdc52017-10-04 15:33:56235 constexpr T& operator*() & { return value(); }
mlamouri53f6b252016-04-19 17:27:01236
237 constexpr const T&& operator*() const&& { return std::move(value()); }
238
Daniel Cheng429dbdc52017-10-04 15:33:56239 constexpr T&& operator*() && { return std::move(value()); }
mlamouri53f6b252016-04-19 17:27:01240
alshabalinf06b07df2016-05-27 08:01:31241 constexpr explicit operator bool() const { return !storage_.is_null_; }
mlamouri53f6b252016-04-19 17:27:01242
mlamouri26204572016-08-10 12:24:15243 constexpr bool has_value() const { return !storage_.is_null_; }
244
Daniel Cheng429dbdc52017-10-04 15:33:56245 constexpr T& value() & {
alshabalinf06b07df2016-05-27 08:01:31246 DCHECK(!storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21247 return storage_.value_;
mlamouri53f6b252016-04-19 17:27:01248 }
249
Daniel Cheng429dbdc52017-10-04 15:33:56250 constexpr const T& value() const & {
alshabalinf06b07df2016-05-27 08:01:31251 DCHECK(!storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21252 return storage_.value_;
mlamouri53f6b252016-04-19 17:27:01253 }
254
Daniel Cheng429dbdc52017-10-04 15:33:56255 constexpr T&& value() && {
alshabalinf06b07df2016-05-27 08:01:31256 DCHECK(!storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21257 return std::move(storage_.value_);
mlamouri53f6b252016-04-19 17:27:01258 }
259
Daniel Cheng429dbdc52017-10-04 15:33:56260 constexpr const T&& value() const && {
alshabalinf06b07df2016-05-27 08:01:31261 DCHECK(!storage_.is_null_);
kwiberg882859a2016-08-16 09:42:21262 return std::move(storage_.value_);
mlamouri53f6b252016-04-19 17:27:01263 }
264
265 template <class U>
266 constexpr T value_or(U&& default_value) const& {
267 // TODO(mlamouri): add the following assert when possible:
268 // static_assert(std::is_copy_constructible<T>::value,
269 // "T must be copy constructible");
270 static_assert(std::is_convertible<U, T>::value,
271 "U must be convertible to T");
alshabalinf06b07df2016-05-27 08:01:31272 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
273 : value();
mlamouri53f6b252016-04-19 17:27:01274 }
275
276 template <class U>
277 T value_or(U&& default_value) && {
278 // TODO(mlamouri): add the following assert when possible:
279 // static_assert(std::is_move_constructible<T>::value,
280 // "T must be move constructible");
281 static_assert(std::is_convertible<U, T>::value,
282 "U must be convertible to T");
alshabalinf06b07df2016-05-27 08:01:31283 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
284 : std::move(value());
mlamouri53f6b252016-04-19 17:27:01285 }
286
287 void swap(Optional& other) {
alshabalinf06b07df2016-05-27 08:01:31288 if (storage_.is_null_ && other.storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01289 return;
290
alshabalinf06b07df2016-05-27 08:01:31291 if (storage_.is_null_ != other.storage_.is_null_) {
292 if (storage_.is_null_) {
kwiberg882859a2016-08-16 09:42:21293 Init(std::move(other.storage_.value_));
mlamouri53f6b252016-04-19 17:27:01294 other.FreeIfNeeded();
295 } else {
kwiberg882859a2016-08-16 09:42:21296 other.Init(std::move(storage_.value_));
mlamouri53f6b252016-04-19 17:27:01297 FreeIfNeeded();
298 }
299 return;
300 }
301
alshabalinf06b07df2016-05-27 08:01:31302 DCHECK(!storage_.is_null_ && !other.storage_.is_null_);
mlamouri53f6b252016-04-19 17:27:01303 using std::swap;
304 swap(**this, *other);
305 }
306
mlamouri26204572016-08-10 12:24:15307 void reset() {
308 FreeIfNeeded();
309 }
310
mlamouri53f6b252016-04-19 17:27:01311 template <class... Args>
312 void emplace(Args&&... args) {
313 FreeIfNeeded();
314 Init(std::forward<Args>(args)...);
315 }
316
317 private:
Hidehiko Abe25bbd5e2017-12-20 04:43:07318 // Accessing template base class's protected member needs explicit
319 // declaration to do so.
320 using internal::OptionalBase<T>::FreeIfNeeded;
321 using internal::OptionalBase<T>::Init;
322 using internal::OptionalBase<T>::InitOrAssign;
323 using internal::OptionalBase<T>::storage_;
mlamouri53f6b252016-04-19 17:27:01324};
325
326template <class T>
327constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
328 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
329}
330
331template <class T>
332constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) {
333 return !(lhs == rhs);
334}
335
336template <class T>
337constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) {
338 return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs);
339}
340
341template <class T>
342constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) {
343 return !(rhs < lhs);
344}
345
346template <class T>
347constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) {
348 return rhs < lhs;
349}
350
351template <class T>
352constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) {
353 return !(lhs < rhs);
354}
355
356template <class T>
Hidehiko Abe25bbd5e2017-12-20 04:43:07357constexpr bool operator==(const Optional<T>& opt, nullopt_t) {
mlamouri53f6b252016-04-19 17:27:01358 return !opt;
359}
360
361template <class T>
Hidehiko Abe25bbd5e2017-12-20 04:43:07362constexpr bool operator==(nullopt_t, const Optional<T>& opt) {
mlamouri53f6b252016-04-19 17:27:01363 return !opt;
364}
365
366template <class T>
Hidehiko Abe25bbd5e2017-12-20 04:43:07367constexpr bool operator!=(const Optional<T>& opt, nullopt_t) {
mlamouri53f6b252016-04-19 17:27:01368 return !!opt;
369}
370
371template <class T>
Hidehiko Abe25bbd5e2017-12-20 04:43:07372constexpr bool operator!=(nullopt_t, const Optional<T>& opt) {
mlamouri53f6b252016-04-19 17:27:01373 return !!opt;
374}
375
376template <class T>
Hidehiko Abe25bbd5e2017-12-20 04:43:07377constexpr bool operator<(const Optional<T>& opt, nullopt_t) {
mlamouri53f6b252016-04-19 17:27:01378 return false;
379}
380
381template <class T>
Hidehiko Abe25bbd5e2017-12-20 04:43:07382constexpr bool operator<(nullopt_t, const Optional<T>& opt) {
mlamouri53f6b252016-04-19 17:27:01383 return !!opt;
384}
385
386template <class T>
Hidehiko Abe25bbd5e2017-12-20 04:43:07387constexpr bool operator<=(const Optional<T>& opt, nullopt_t) {
mlamouri53f6b252016-04-19 17:27:01388 return !opt;
389}
390
391template <class T>
Hidehiko Abe25bbd5e2017-12-20 04:43:07392constexpr bool operator<=(nullopt_t, const Optional<T>& opt) {
mlamouri53f6b252016-04-19 17:27:01393 return true;
394}
395
396template <class T>
Hidehiko Abe25bbd5e2017-12-20 04:43:07397constexpr bool operator>(const Optional<T>& opt, nullopt_t) {
mlamouri53f6b252016-04-19 17:27:01398 return !!opt;
399}
400
401template <class T>
Hidehiko Abe25bbd5e2017-12-20 04:43:07402constexpr bool operator>(nullopt_t, const Optional<T>& opt) {
mlamouri53f6b252016-04-19 17:27:01403 return false;
404}
405
406template <class T>
Hidehiko Abe25bbd5e2017-12-20 04:43:07407constexpr bool operator>=(const Optional<T>& opt, nullopt_t) {
mlamouri53f6b252016-04-19 17:27:01408 return true;
409}
410
411template <class T>
Hidehiko Abe25bbd5e2017-12-20 04:43:07412constexpr bool operator>=(nullopt_t, const Optional<T>& opt) {
mlamouri53f6b252016-04-19 17:27:01413 return !opt;
414}
415
416template <class T>
417constexpr bool operator==(const Optional<T>& opt, const T& value) {
418 return opt != nullopt ? *opt == value : false;
419}
420
421template <class T>
422constexpr bool operator==(const T& value, const Optional<T>& opt) {
423 return opt == value;
424}
425
426template <class T>
427constexpr bool operator!=(const Optional<T>& opt, const T& value) {
428 return !(opt == value);
429}
430
431template <class T>
432constexpr bool operator!=(const T& value, const Optional<T>& opt) {
433 return !(opt == value);
434}
435
436template <class T>
437constexpr bool operator<(const Optional<T>& opt, const T& value) {
438 return opt != nullopt ? *opt < value : true;
439}
440
441template <class T>
442constexpr bool operator<(const T& value, const Optional<T>& opt) {
443 return opt != nullopt ? value < *opt : false;
444}
445
446template <class T>
447constexpr bool operator<=(const Optional<T>& opt, const T& value) {
448 return !(opt > value);
449}
450
451template <class T>
452constexpr bool operator<=(const T& value, const Optional<T>& opt) {
453 return !(value > opt);
454}
455
456template <class T>
457constexpr bool operator>(const Optional<T>& opt, const T& value) {
458 return value < opt;
459}
460
461template <class T>
462constexpr bool operator>(const T& value, const Optional<T>& opt) {
463 return opt < value;
464}
465
466template <class T>
467constexpr bool operator>=(const Optional<T>& opt, const T& value) {
468 return !(opt < value);
469}
470
471template <class T>
472constexpr bool operator>=(const T& value, const Optional<T>& opt) {
473 return !(value < opt);
474}
475
476template <class T>
477constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
478 return Optional<typename std::decay<T>::type>(std::forward<T>(value));
479}
480
481template <class T>
482void swap(Optional<T>& lhs, Optional<T>& rhs) {
483 lhs.swap(rhs);
484}
485
486} // namespace base
487
488namespace std {
489
490template <class T>
491struct hash<base::Optional<T>> {
492 size_t operator()(const base::Optional<T>& opt) const {
493 return opt == base::nullopt ? 0 : std::hash<T>()(*opt);
494 }
495};
496
497} // namespace std
498
499#endif // BASE_OPTIONAL_H_