OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef BASE_OPTIONAL_H_ | 5 #ifndef BASE_OPTIONAL_H_ |
6 #define BASE_OPTIONAL_H_ | 6 #define BASE_OPTIONAL_H_ |
7 | 7 |
8 #include <type_traits> | 8 #include <type_traits> |
9 | 9 |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/memory/aligned_memory.h" | 11 #include "base/memory/aligned_memory.h" |
| 12 #include "base/template_util.h" |
12 | 13 |
13 namespace base { | 14 namespace base { |
14 | 15 |
15 // Specification: | 16 // Specification: |
16 // http://en.cppreference.com/w/cpp/utility/optional/in_place_t | 17 // http://en.cppreference.com/w/cpp/utility/optional/in_place_t |
17 struct in_place_t {}; | 18 struct in_place_t {}; |
18 | 19 |
19 // Specification: | 20 // Specification: |
20 // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t | 21 // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t |
21 struct nullopt_t { | 22 struct nullopt_t { |
22 constexpr explicit nullopt_t(int) {} | 23 constexpr explicit nullopt_t(int) {} |
23 }; | 24 }; |
24 | 25 |
25 // Specification: | 26 // Specification: |
26 // http://en.cppreference.com/w/cpp/utility/optional/in_place | 27 // http://en.cppreference.com/w/cpp/utility/optional/in_place |
27 constexpr in_place_t in_place = {}; | 28 constexpr in_place_t in_place = {}; |
28 | 29 |
29 // Specification: | 30 // Specification: |
30 // http://en.cppreference.com/w/cpp/utility/optional/nullopt | 31 // http://en.cppreference.com/w/cpp/utility/optional/nullopt |
31 constexpr nullopt_t nullopt(0); | 32 constexpr nullopt_t nullopt(0); |
32 | 33 |
| 34 namespace internal { |
| 35 |
| 36 template <typename T, bool = base::is_trivially_destructible<T>::value> |
| 37 struct 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 |
| 49 template <typename T> |
| 50 struct 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 |
33 // base::Optional is a Chromium version of the C++17 optional class: | 64 // base::Optional is a Chromium version of the C++17 optional class: |
34 // std::optional documentation: | 65 // std::optional documentation: |
35 // http://en.cppreference.com/w/cpp/utility/optional | 66 // http://en.cppreference.com/w/cpp/utility/optional |
36 // Chromium documentation: | 67 // Chromium documentation: |
37 // https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md | 68 // https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md |
38 // | 69 // |
39 // These are the differences between the specification and the implementation: | 70 // These are the differences between the specification and the implementation: |
40 // - The constructor and emplace method using initializer_list are not | 71 // - The constructor and emplace method using initializer_list are not |
41 // implemented because 'initializer_list' is banned from Chromium. | 72 // implemented because 'initializer_list' is banned from Chromium. |
42 // - Constructors do not use 'constexpr' as it is a C++14 extension. | 73 // - Constructors do not use 'constexpr' as it is a C++14 extension. |
43 // - 'constexpr' might be missing in some places for reasons specified locally. | 74 // - 'constexpr' might be missing in some places for reasons specified locally. |
44 // - No exceptions are thrown, because they are banned from Chromium. | 75 // - No exceptions are thrown, because they are banned from Chromium. |
45 // - All the non-members are in the 'base' namespace instead of 'std'. | 76 // - All the non-members are in the 'base' namespace instead of 'std'. |
46 template <typename T> | 77 template <typename T> |
47 class Optional { | 78 class Optional { |
48 public: | 79 public: |
| 80 using value_type = T; |
| 81 |
49 constexpr Optional() = default; | 82 constexpr Optional() = default; |
50 Optional(base::nullopt_t) : Optional() {} | 83 Optional(base::nullopt_t) : Optional() {} |
51 | 84 |
52 Optional(const Optional& other) { | 85 Optional(const Optional& other) { |
53 if (!other.is_null_) | 86 if (!other.storage_.is_null_) |
54 Init(other.value()); | 87 Init(other.value()); |
55 } | 88 } |
56 | 89 |
57 Optional(Optional&& other) { | 90 Optional(Optional&& other) { |
58 if (!other.is_null_) | 91 if (!other.storage_.is_null_) |
59 Init(std::move(other.value())); | 92 Init(std::move(other.value())); |
60 } | 93 } |
61 | 94 |
62 Optional(const T& value) { Init(value); } | 95 Optional(const T& value) { Init(value); } |
63 | 96 |
64 Optional(T&& value) { Init(std::move(value)); } | 97 Optional(T&& value) { Init(std::move(value)); } |
65 | 98 |
66 template <class... Args> | 99 template <class... Args> |
67 explicit Optional(base::in_place_t, Args&&... args) { | 100 explicit Optional(base::in_place_t, Args&&... args) { |
68 emplace(std::forward<Args>(args)...); | 101 emplace(std::forward<Args>(args)...); |
69 } | 102 } |
70 | 103 |
71 ~Optional() { | 104 ~Optional() = default; |
72 // TODO(mlamouri): use is_trivially_destructible<T>::value when possible. | |
73 FreeIfNeeded(); | |
74 } | |
75 | 105 |
76 Optional& operator=(base::nullopt_t) { | 106 Optional& operator=(base::nullopt_t) { |
77 FreeIfNeeded(); | 107 FreeIfNeeded(); |
78 return *this; | 108 return *this; |
79 } | 109 } |
80 | 110 |
81 Optional& operator=(const Optional& other) { | 111 Optional& operator=(const Optional& other) { |
82 if (other.is_null_) { | 112 if (other.storage_.is_null_) { |
83 FreeIfNeeded(); | 113 FreeIfNeeded(); |
84 return *this; | 114 return *this; |
85 } | 115 } |
86 | 116 |
87 InitOrAssign(other.value()); | 117 InitOrAssign(other.value()); |
88 return *this; | 118 return *this; |
89 } | 119 } |
90 | 120 |
91 Optional& operator=(Optional&& other) { | 121 Optional& operator=(Optional&& other) { |
92 if (other.is_null_) { | 122 if (other.storage_.is_null_) { |
93 FreeIfNeeded(); | 123 FreeIfNeeded(); |
94 return *this; | 124 return *this; |
95 } | 125 } |
96 | 126 |
97 InitOrAssign(std::move(other.value())); | 127 InitOrAssign(std::move(other.value())); |
98 return *this; | 128 return *this; |
99 } | 129 } |
100 | 130 |
101 template <class U> | 131 template <class U> |
102 typename std::enable_if<std::is_same<std::decay<U>, T>::value, | 132 typename std::enable_if<std::is_same<std::decay<U>, T>::value, |
103 Optional&>::type | 133 Optional&>::type |
104 operator=(U&& value) { | 134 operator=(U&& value) { |
105 InitOrAssign(std::forward<U>(value)); | 135 InitOrAssign(std::forward<U>(value)); |
106 return *this; | 136 return *this; |
107 } | 137 } |
108 | 138 |
109 // TODO(mlamouri): can't use 'constexpr' with DCHECK. | 139 // TODO(mlamouri): can't use 'constexpr' with DCHECK. |
110 const T* operator->() const { | 140 const T* operator->() const { |
111 DCHECK(!is_null_); | 141 DCHECK(!storage_.is_null_); |
112 return &value(); | 142 return &value(); |
113 } | 143 } |
114 | 144 |
115 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was | 145 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
116 // meant to be 'constexpr const'. | 146 // meant to be 'constexpr const'. |
117 T* operator->() { | 147 T* operator->() { |
118 DCHECK(!is_null_); | 148 DCHECK(!storage_.is_null_); |
119 return &value(); | 149 return &value(); |
120 } | 150 } |
121 | 151 |
122 constexpr const T& operator*() const& { return value(); } | 152 constexpr const T& operator*() const& { return value(); } |
123 | 153 |
124 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was | 154 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
125 // meant to be 'constexpr const'. | 155 // meant to be 'constexpr const'. |
126 T& operator*() & { return value(); } | 156 T& operator*() & { return value(); } |
127 | 157 |
128 constexpr const T&& operator*() const&& { return std::move(value()); } | 158 constexpr const T&& operator*() const&& { return std::move(value()); } |
129 | 159 |
130 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was | 160 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
131 // meant to be 'constexpr const'. | 161 // meant to be 'constexpr const'. |
132 T&& operator*() && { return std::move(value()); } | 162 T&& operator*() && { return std::move(value()); } |
133 | 163 |
134 constexpr explicit operator bool() const { return !is_null_; } | 164 constexpr explicit operator bool() const { return !storage_.is_null_; } |
135 | 165 |
136 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was | 166 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
137 // meant to be 'constexpr const'. | 167 // meant to be 'constexpr const'. |
138 T& value() & { | 168 T& value() & { |
139 DCHECK(!is_null_); | 169 DCHECK(!storage_.is_null_); |
140 return *buffer_.template data_as<T>(); | 170 return *storage_.buffer_.template data_as<T>(); |
141 } | 171 } |
142 | 172 |
143 // TODO(mlamouri): can't use 'constexpr' with DCHECK. | 173 // TODO(mlamouri): can't use 'constexpr' with DCHECK. |
144 const T& value() const& { | 174 const T& value() const& { |
145 DCHECK(!is_null_); | 175 DCHECK(!storage_.is_null_); |
146 return *buffer_.template data_as<T>(); | 176 return *storage_.buffer_.template data_as<T>(); |
147 } | 177 } |
148 | 178 |
149 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was | 179 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
150 // meant to be 'constexpr const'. | 180 // meant to be 'constexpr const'. |
151 T&& value() && { | 181 T&& value() && { |
152 DCHECK(!is_null_); | 182 DCHECK(!storage_.is_null_); |
153 return std::move(*buffer_.template data_as<T>()); | 183 return std::move(*storage_.buffer_.template data_as<T>()); |
154 } | 184 } |
155 | 185 |
156 // TODO(mlamouri): can't use 'constexpr' with DCHECK. | 186 // TODO(mlamouri): can't use 'constexpr' with DCHECK. |
157 const T&& value() const&& { | 187 const T&& value() const&& { |
158 DCHECK(!is_null_); | 188 DCHECK(!storage_.is_null_); |
159 return std::move(*buffer_.template data_as<T>()); | 189 return std::move(*storage_.buffer_.template data_as<T>()); |
160 } | 190 } |
161 | 191 |
162 template <class U> | 192 template <class U> |
163 constexpr T value_or(U&& default_value) const& { | 193 constexpr T value_or(U&& default_value) const& { |
164 // TODO(mlamouri): add the following assert when possible: | 194 // TODO(mlamouri): add the following assert when possible: |
165 // static_assert(std::is_copy_constructible<T>::value, | 195 // static_assert(std::is_copy_constructible<T>::value, |
166 // "T must be copy constructible"); | 196 // "T must be copy constructible"); |
167 static_assert(std::is_convertible<U, T>::value, | 197 static_assert(std::is_convertible<U, T>::value, |
168 "U must be convertible to T"); | 198 "U must be convertible to T"); |
169 return is_null_ ? static_cast<T>(std::forward<U>(default_value)) : value(); | 199 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value)) |
| 200 : value(); |
170 } | 201 } |
171 | 202 |
172 template <class U> | 203 template <class U> |
173 T value_or(U&& default_value) && { | 204 T value_or(U&& default_value) && { |
174 // TODO(mlamouri): add the following assert when possible: | 205 // TODO(mlamouri): add the following assert when possible: |
175 // static_assert(std::is_move_constructible<T>::value, | 206 // static_assert(std::is_move_constructible<T>::value, |
176 // "T must be move constructible"); | 207 // "T must be move constructible"); |
177 static_assert(std::is_convertible<U, T>::value, | 208 static_assert(std::is_convertible<U, T>::value, |
178 "U must be convertible to T"); | 209 "U must be convertible to T"); |
179 return is_null_ ? static_cast<T>(std::forward<U>(default_value)) | 210 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value)) |
180 : std::move(value()); | 211 : std::move(value()); |
181 } | 212 } |
182 | 213 |
183 void swap(Optional& other) { | 214 void swap(Optional& other) { |
184 if (is_null_ && other.is_null_) | 215 if (storage_.is_null_ && other.storage_.is_null_) |
185 return; | 216 return; |
186 | 217 |
187 if (is_null_ != other.is_null_) { | 218 if (storage_.is_null_ != other.storage_.is_null_) { |
188 if (is_null_) { | 219 if (storage_.is_null_) { |
189 Init(std::move(*other.buffer_.template data_as<T>())); | 220 Init(std::move(*other.storage_.buffer_.template data_as<T>())); |
190 other.FreeIfNeeded(); | 221 other.FreeIfNeeded(); |
191 } else { | 222 } else { |
192 other.Init(std::move(*buffer_.template data_as<T>())); | 223 other.Init(std::move(*storage_.buffer_.template data_as<T>())); |
193 FreeIfNeeded(); | 224 FreeIfNeeded(); |
194 } | 225 } |
195 return; | 226 return; |
196 } | 227 } |
197 | 228 |
198 DCHECK(!is_null_ && !other.is_null_); | 229 DCHECK(!storage_.is_null_ && !other.storage_.is_null_); |
199 using std::swap; | 230 using std::swap; |
200 swap(**this, *other); | 231 swap(**this, *other); |
201 } | 232 } |
202 | 233 |
203 template <class... Args> | 234 template <class... Args> |
204 void emplace(Args&&... args) { | 235 void emplace(Args&&... args) { |
205 FreeIfNeeded(); | 236 FreeIfNeeded(); |
206 Init(std::forward<Args>(args)...); | 237 Init(std::forward<Args>(args)...); |
207 } | 238 } |
208 | 239 |
209 private: | 240 private: |
210 void Init(const T& value) { | 241 void Init(const T& value) { |
211 DCHECK(is_null_); | 242 DCHECK(storage_.is_null_); |
212 new (buffer_.template data_as<T>()) T(value); | 243 new (storage_.buffer_.template data_as<T>()) T(value); |
213 is_null_ = false; | 244 storage_.is_null_ = false; |
214 } | 245 } |
215 | 246 |
216 void Init(T&& value) { | 247 void Init(T&& value) { |
217 DCHECK(is_null_); | 248 DCHECK(storage_.is_null_); |
218 new (buffer_.template data_as<T>()) T(std::move(value)); | 249 new (storage_.buffer_.template data_as<T>()) T(std::move(value)); |
219 is_null_ = false; | 250 storage_.is_null_ = false; |
220 } | 251 } |
221 | 252 |
222 template <class... Args> | 253 template <class... Args> |
223 void Init(Args&&... args) { | 254 void Init(Args&&... args) { |
224 DCHECK(is_null_); | 255 DCHECK(storage_.is_null_); |
225 new (buffer_.template data_as<T>()) T(std::forward<Args>(args)...); | 256 new (storage_.buffer_.template data_as<T>()) T(std::forward<Args>(args)...); |
226 is_null_ = false; | 257 storage_.is_null_ = false; |
227 } | 258 } |
228 | 259 |
229 void InitOrAssign(const T& value) { | 260 void InitOrAssign(const T& value) { |
230 if (is_null_) | 261 if (storage_.is_null_) |
231 Init(value); | 262 Init(value); |
232 else | 263 else |
233 *buffer_.template data_as<T>() = value; | 264 *storage_.buffer_.template data_as<T>() = value; |
234 } | 265 } |
235 | 266 |
236 void InitOrAssign(T&& value) { | 267 void InitOrAssign(T&& value) { |
237 if (is_null_) | 268 if (storage_.is_null_) |
238 Init(std::move(value)); | 269 Init(std::move(value)); |
239 else | 270 else |
240 *buffer_.template data_as<T>() = std::move(value); | 271 *storage_.buffer_.template data_as<T>() = std::move(value); |
241 } | 272 } |
242 | 273 |
243 void FreeIfNeeded() { | 274 void FreeIfNeeded() { |
244 if (is_null_) | 275 if (storage_.is_null_) |
245 return; | 276 return; |
246 buffer_.template data_as<T>()->~T(); | 277 storage_.buffer_.template data_as<T>()->~T(); |
247 is_null_ = true; | 278 storage_.is_null_ = true; |
248 } | 279 } |
249 | 280 |
250 bool is_null_ = true; | 281 internal::OptionalStorage<T> storage_; |
251 base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_; | |
252 }; | 282 }; |
253 | 283 |
254 template <class T> | 284 template <class T> |
255 constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) { | 285 constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) { |
256 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs); | 286 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs); |
257 } | 287 } |
258 | 288 |
259 template <class T> | 289 template <class T> |
260 constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) { | 290 constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) { |
261 return !(lhs == rhs); | 291 return !(lhs == rhs); |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
418 template <class T> | 448 template <class T> |
419 struct hash<base::Optional<T>> { | 449 struct hash<base::Optional<T>> { |
420 size_t operator()(const base::Optional<T>& opt) const { | 450 size_t operator()(const base::Optional<T>& opt) const { |
421 return opt == base::nullopt ? 0 : std::hash<T>()(*opt); | 451 return opt == base::nullopt ? 0 : std::hash<T>()(*opt); |
422 } | 452 } |
423 }; | 453 }; |
424 | 454 |
425 } // namespace std | 455 } // namespace std |
426 | 456 |
427 #endif // BASE_OPTIONAL_H_ | 457 #endif // BASE_OPTIONAL_H_ |
OLD | NEW |