blob: 988767cfe32cf4916bf5aa3272ebd271f1dc73c1 [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"
11#include "base/memory/aligned_memory.h"
alshabalinf06b07df2016-05-27 08:01:3112#include "base/template_util.h"
mlamouri53f6b252016-04-19 17:27:0113
14namespace base {
15
16// Specification:
17// http://en.cppreference.com/w/cpp/utility/optional/in_place_t
18struct in_place_t {};
19
20// Specification:
21// http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
22struct nullopt_t {
23 constexpr explicit nullopt_t(int) {}
24};
25
26// Specification:
27// http://en.cppreference.com/w/cpp/utility/optional/in_place
28constexpr in_place_t in_place = {};
29
30// Specification:
31// http://en.cppreference.com/w/cpp/utility/optional/nullopt
32constexpr nullopt_t nullopt(0);
33
alshabalinf06b07df2016-05-27 08:01:3134namespace internal {
35
36template <typename T, bool = base::is_trivially_destructible<T>::value>
37struct OptionalStorage {
38 // When T is not trivially destructible we must call its
39 // destructor before deallocating its memory.
40 ~OptionalStorage() {
41 if (!is_null_)
42 buffer_.template data_as<T>()->~T();
43 }
44
45 bool is_null_ = true;
46 base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_;
47};
48
49template <typename T>
50struct OptionalStorage<T, true> {
51 // When T is trivially destructible (i.e. its destructor does nothing)
52 // there is no need to call it.
53 // Since |base::AlignedMemory| is just an array its destructor
54 // is trivial. Explicitly defaulting the destructor means it's not
55 // user-provided. All of this together make this destructor trivial.
56 ~OptionalStorage() = default;
57
58 bool is_null_ = true;
59 base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_;
60};
61
62} // namespace internal
63
mlamouri53f6b252016-04-19 17:27:0164// base::Optional is a Chromium version of the C++17 optional class:
65// std::optional documentation:
66// http://en.cppreference.com/w/cpp/utility/optional
67// Chromium documentation:
68// https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
69//
70// These are the differences between the specification and the implementation:
71// - The constructor and emplace method using initializer_list are not
72// implemented because 'initializer_list' is banned from Chromium.
73// - Constructors do not use 'constexpr' as it is a C++14 extension.
74// - 'constexpr' might be missing in some places for reasons specified locally.
75// - No exceptions are thrown, because they are banned from Chromium.
76// - All the non-members are in the 'base' namespace instead of 'std'.
77template <typename T>
78class Optional {
79 public:
alshabalinf06b07df2016-05-27 08:01:3180 using value_type = T;
81
mlamouri53f6b252016-04-19 17:27:0182 constexpr Optional() = default;
83 Optional(base::nullopt_t) : Optional() {}
84
85 Optional(const Optional& other) {
alshabalinf06b07df2016-05-27 08:01:3186 if (!other.storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:0187 Init(other.value());
88 }
89
90 Optional(Optional&& other) {
alshabalinf06b07df2016-05-27 08:01:3191 if (!other.storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:0192 Init(std::move(other.value()));
93 }
94
95 Optional(const T& value) { Init(value); }
96
97 Optional(T&& value) { Init(std::move(value)); }
98
99 template <class... Args>
100 explicit Optional(base::in_place_t, Args&&... args) {
101 emplace(std::forward<Args>(args)...);
102 }
103
alshabalinf06b07df2016-05-27 08:01:31104 ~Optional() = default;
mlamouri53f6b252016-04-19 17:27:01105
106 Optional& operator=(base::nullopt_t) {
107 FreeIfNeeded();
108 return *this;
109 }
110
111 Optional& operator=(const Optional& other) {
alshabalinf06b07df2016-05-27 08:01:31112 if (other.storage_.is_null_) {
mlamouri53f6b252016-04-19 17:27:01113 FreeIfNeeded();
114 return *this;
115 }
116
117 InitOrAssign(other.value());
118 return *this;
119 }
120
121 Optional& operator=(Optional&& other) {
alshabalinf06b07df2016-05-27 08:01:31122 if (other.storage_.is_null_) {
mlamouri53f6b252016-04-19 17:27:01123 FreeIfNeeded();
124 return *this;
125 }
126
127 InitOrAssign(std::move(other.value()));
128 return *this;
129 }
130
131 template <class U>
132 typename std::enable_if<std::is_same<std::decay<U>, T>::value,
133 Optional&>::type
134 operator=(U&& value) {
135 InitOrAssign(std::forward<U>(value));
136 return *this;
137 }
138
139 // TODO(mlamouri): can't use 'constexpr' with DCHECK.
140 const T* operator->() const {
alshabalinf06b07df2016-05-27 08:01:31141 DCHECK(!storage_.is_null_);
mlamouri53f6b252016-04-19 17:27:01142 return &value();
143 }
144
145 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
146 // meant to be 'constexpr const'.
147 T* operator->() {
alshabalinf06b07df2016-05-27 08:01:31148 DCHECK(!storage_.is_null_);
mlamouri53f6b252016-04-19 17:27:01149 return &value();
150 }
151
152 constexpr const T& operator*() const& { return value(); }
153
154 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
155 // meant to be 'constexpr const'.
156 T& operator*() & { return value(); }
157
158 constexpr const T&& operator*() const&& { return std::move(value()); }
159
160 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
161 // meant to be 'constexpr const'.
162 T&& operator*() && { return std::move(value()); }
163
alshabalinf06b07df2016-05-27 08:01:31164 constexpr explicit operator bool() const { return !storage_.is_null_; }
mlamouri53f6b252016-04-19 17:27:01165
166 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
167 // meant to be 'constexpr const'.
168 T& value() & {
alshabalinf06b07df2016-05-27 08:01:31169 DCHECK(!storage_.is_null_);
170 return *storage_.buffer_.template data_as<T>();
mlamouri53f6b252016-04-19 17:27:01171 }
172
173 // TODO(mlamouri): can't use 'constexpr' with DCHECK.
174 const T& value() const& {
alshabalinf06b07df2016-05-27 08:01:31175 DCHECK(!storage_.is_null_);
176 return *storage_.buffer_.template data_as<T>();
mlamouri53f6b252016-04-19 17:27:01177 }
178
179 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
180 // meant to be 'constexpr const'.
181 T&& value() && {
alshabalinf06b07df2016-05-27 08:01:31182 DCHECK(!storage_.is_null_);
183 return std::move(*storage_.buffer_.template data_as<T>());
mlamouri53f6b252016-04-19 17:27:01184 }
185
186 // TODO(mlamouri): can't use 'constexpr' with DCHECK.
187 const T&& value() const&& {
alshabalinf06b07df2016-05-27 08:01:31188 DCHECK(!storage_.is_null_);
189 return std::move(*storage_.buffer_.template data_as<T>());
mlamouri53f6b252016-04-19 17:27:01190 }
191
192 template <class U>
193 constexpr T value_or(U&& default_value) const& {
194 // TODO(mlamouri): add the following assert when possible:
195 // static_assert(std::is_copy_constructible<T>::value,
196 // "T must be copy constructible");
197 static_assert(std::is_convertible<U, T>::value,
198 "U must be convertible to T");
alshabalinf06b07df2016-05-27 08:01:31199 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
200 : value();
mlamouri53f6b252016-04-19 17:27:01201 }
202
203 template <class U>
204 T value_or(U&& default_value) && {
205 // TODO(mlamouri): add the following assert when possible:
206 // static_assert(std::is_move_constructible<T>::value,
207 // "T must be move constructible");
208 static_assert(std::is_convertible<U, T>::value,
209 "U must be convertible to T");
alshabalinf06b07df2016-05-27 08:01:31210 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
211 : std::move(value());
mlamouri53f6b252016-04-19 17:27:01212 }
213
214 void swap(Optional& other) {
alshabalinf06b07df2016-05-27 08:01:31215 if (storage_.is_null_ && other.storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01216 return;
217
alshabalinf06b07df2016-05-27 08:01:31218 if (storage_.is_null_ != other.storage_.is_null_) {
219 if (storage_.is_null_) {
220 Init(std::move(*other.storage_.buffer_.template data_as<T>()));
mlamouri53f6b252016-04-19 17:27:01221 other.FreeIfNeeded();
222 } else {
alshabalinf06b07df2016-05-27 08:01:31223 other.Init(std::move(*storage_.buffer_.template data_as<T>()));
mlamouri53f6b252016-04-19 17:27:01224 FreeIfNeeded();
225 }
226 return;
227 }
228
alshabalinf06b07df2016-05-27 08:01:31229 DCHECK(!storage_.is_null_ && !other.storage_.is_null_);
mlamouri53f6b252016-04-19 17:27:01230 using std::swap;
231 swap(**this, *other);
232 }
233
234 template <class... Args>
235 void emplace(Args&&... args) {
236 FreeIfNeeded();
237 Init(std::forward<Args>(args)...);
238 }
239
240 private:
241 void Init(const T& value) {
alshabalinf06b07df2016-05-27 08:01:31242 DCHECK(storage_.is_null_);
243 new (storage_.buffer_.template data_as<T>()) T(value);
244 storage_.is_null_ = false;
mlamouri53f6b252016-04-19 17:27:01245 }
246
247 void Init(T&& value) {
alshabalinf06b07df2016-05-27 08:01:31248 DCHECK(storage_.is_null_);
249 new (storage_.buffer_.template data_as<T>()) T(std::move(value));
250 storage_.is_null_ = false;
mlamouri53f6b252016-04-19 17:27:01251 }
252
253 template <class... Args>
254 void Init(Args&&... args) {
alshabalinf06b07df2016-05-27 08:01:31255 DCHECK(storage_.is_null_);
256 new (storage_.buffer_.template data_as<T>()) T(std::forward<Args>(args)...);
257 storage_.is_null_ = false;
mlamouri53f6b252016-04-19 17:27:01258 }
259
260 void InitOrAssign(const T& value) {
alshabalinf06b07df2016-05-27 08:01:31261 if (storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01262 Init(value);
263 else
alshabalinf06b07df2016-05-27 08:01:31264 *storage_.buffer_.template data_as<T>() = value;
mlamouri53f6b252016-04-19 17:27:01265 }
266
267 void InitOrAssign(T&& value) {
alshabalinf06b07df2016-05-27 08:01:31268 if (storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01269 Init(std::move(value));
270 else
alshabalinf06b07df2016-05-27 08:01:31271 *storage_.buffer_.template data_as<T>() = std::move(value);
mlamouri53f6b252016-04-19 17:27:01272 }
273
274 void FreeIfNeeded() {
alshabalinf06b07df2016-05-27 08:01:31275 if (storage_.is_null_)
mlamouri53f6b252016-04-19 17:27:01276 return;
alshabalinf06b07df2016-05-27 08:01:31277 storage_.buffer_.template data_as<T>()->~T();
278 storage_.is_null_ = true;
mlamouri53f6b252016-04-19 17:27:01279 }
280
alshabalinf06b07df2016-05-27 08:01:31281 internal::OptionalStorage<T> storage_;
mlamouri53f6b252016-04-19 17:27:01282};
283
284template <class T>
285constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
286 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
287}
288
289template <class T>
290constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) {
291 return !(lhs == rhs);
292}
293
294template <class T>
295constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) {
296 return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs);
297}
298
299template <class T>
300constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) {
301 return !(rhs < lhs);
302}
303
304template <class T>
305constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) {
306 return rhs < lhs;
307}
308
309template <class T>
310constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) {
311 return !(lhs < rhs);
312}
313
314template <class T>
315constexpr bool operator==(const Optional<T>& opt, base::nullopt_t) {
316 return !opt;
317}
318
319template <class T>
320constexpr bool operator==(base::nullopt_t, const Optional<T>& opt) {
321 return !opt;
322}
323
324template <class T>
325constexpr bool operator!=(const Optional<T>& opt, base::nullopt_t) {
326 return !!opt;
327}
328
329template <class T>
330constexpr bool operator!=(base::nullopt_t, const Optional<T>& opt) {
331 return !!opt;
332}
333
334template <class T>
335constexpr bool operator<(const Optional<T>& opt, base::nullopt_t) {
336 return false;
337}
338
339template <class T>
340constexpr bool operator<(base::nullopt_t, const Optional<T>& opt) {
341 return !!opt;
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 true;
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 false;
362}
363
364template <class T>
365constexpr bool operator>=(const Optional<T>& opt, base::nullopt_t) {
366 return true;
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, const T& value) {
376 return opt != nullopt ? *opt == value : false;
377}
378
379template <class T>
380constexpr bool operator==(const T& value, const Optional<T>& opt) {
381 return opt == value;
382}
383
384template <class T>
385constexpr bool operator!=(const Optional<T>& opt, const T& value) {
386 return !(opt == value);
387}
388
389template <class T>
390constexpr bool operator!=(const T& value, const Optional<T>& opt) {
391 return !(opt == value);
392}
393
394template <class T>
395constexpr bool operator<(const Optional<T>& opt, const T& value) {
396 return opt != nullopt ? *opt < value : true;
397}
398
399template <class T>
400constexpr bool operator<(const T& value, const Optional<T>& opt) {
401 return opt != nullopt ? value < *opt : false;
402}
403
404template <class T>
405constexpr bool operator<=(const Optional<T>& opt, const T& value) {
406 return !(opt > value);
407}
408
409template <class T>
410constexpr bool operator<=(const T& value, const Optional<T>& opt) {
411 return !(value > opt);
412}
413
414template <class T>
415constexpr bool operator>(const Optional<T>& opt, const T& value) {
416 return value < opt;
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 < value);
427}
428
429template <class T>
430constexpr bool operator>=(const T& value, const Optional<T>& opt) {
431 return !(value < opt);
432}
433
434template <class T>
435constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
436 return Optional<typename std::decay<T>::type>(std::forward<T>(value));
437}
438
439template <class T>
440void swap(Optional<T>& lhs, Optional<T>& rhs) {
441 lhs.swap(rhs);
442}
443
444} // namespace base
445
446namespace std {
447
448template <class T>
449struct hash<base::Optional<T>> {
450 size_t operator()(const base::Optional<T>& opt) const {
451 return opt == base::nullopt ? 0 : std::hash<T>()(*opt);
452 }
453};
454
455} // namespace std
456
457#endif // BASE_OPTIONAL_H_