OLD | NEW |
| (Empty) |
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 | |
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" | |
12 | |
13 namespace base { | |
14 | |
15 // Specification: | |
16 // http://en.cppreference.com/w/cpp/utility/optional/in_place_t | |
17 struct in_place_t {}; | |
18 | |
19 // Specification: | |
20 // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t | |
21 struct nullopt_t { | |
22 constexpr explicit nullopt_t(int) {} | |
23 }; | |
24 | |
25 // Specification: | |
26 // http://en.cppreference.com/w/cpp/utility/optional/in_place | |
27 constexpr in_place_t in_place = {}; | |
28 | |
29 // Specification: | |
30 // http://en.cppreference.com/w/cpp/utility/optional/nullopt | |
31 constexpr nullopt_t nullopt(0); | |
32 | |
33 // base::Optional is a Chromium version of the C++17 optional class: | |
34 // std::optional documentation: | |
35 // http://en.cppreference.com/w/cpp/utility/optional | |
36 // Chromium documentation: | |
37 // https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md | |
38 // | |
39 // These are the differences between the specification and the implementation: | |
40 // - The constructor and emplace method using initializer_list are not | |
41 // implemented because 'initializer_list' is banned from Chromium. | |
42 // - Constructors do not use 'constexpr' as it is a C++14 extension. | |
43 // - 'constexpr' might be missing in some places for reasons specified locally. | |
44 // - No exceptions are thrown, because they are banned from Chromium. | |
45 // - All the non-members are in the 'base' namespace instead of 'std'. | |
46 template <typename T> | |
47 class Optional { | |
48 public: | |
49 constexpr Optional() = default; | |
50 Optional(base::nullopt_t) : Optional() {} | |
51 | |
52 Optional(const Optional& other) { | |
53 if (!other.is_null_) | |
54 Init(other.value()); | |
55 } | |
56 | |
57 Optional(Optional&& other) { | |
58 if (!other.is_null_) | |
59 Init(std::move(other.value())); | |
60 } | |
61 | |
62 Optional(const T& value) { Init(value); } | |
63 | |
64 Optional(T&& value) { Init(std::move(value)); } | |
65 | |
66 template <class... Args> | |
67 explicit Optional(base::in_place_t, Args&&... args) { | |
68 emplace(std::forward<Args>(args)...); | |
69 } | |
70 | |
71 ~Optional() { | |
72 // TODO(mlamouri): use is_trivially_destructible<T>::value when possible. | |
73 FreeIfNeeded(); | |
74 } | |
75 | |
76 Optional& operator=(base::nullopt_t) { | |
77 FreeIfNeeded(); | |
78 return *this; | |
79 } | |
80 | |
81 Optional& operator=(const Optional& other) { | |
82 if (other.is_null_) { | |
83 FreeIfNeeded(); | |
84 return *this; | |
85 } | |
86 | |
87 InitOrAssign(other.value()); | |
88 return *this; | |
89 } | |
90 | |
91 Optional& operator=(Optional&& other) { | |
92 if (other.is_null_) { | |
93 FreeIfNeeded(); | |
94 return *this; | |
95 } | |
96 | |
97 InitOrAssign(std::move(other.value())); | |
98 return *this; | |
99 } | |
100 | |
101 template <class U> | |
102 typename std::enable_if<std::is_same<std::decay<U>, T>::value, | |
103 Optional&>::type | |
104 operator=(U&& value) { | |
105 InitOrAssign(std::forward<U>(value)); | |
106 return *this; | |
107 } | |
108 | |
109 // TODO(mlamouri): can't use 'constexpr' with DCHECK. | |
110 const T* operator->() const { | |
111 DCHECK(!is_null_); | |
112 return &value(); | |
113 } | |
114 | |
115 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was | |
116 // meant to be 'constexpr const'. | |
117 T* operator->() { | |
118 DCHECK(!is_null_); | |
119 return &value(); | |
120 } | |
121 | |
122 constexpr const T& operator*() const& { return value(); } | |
123 | |
124 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was | |
125 // meant to be 'constexpr const'. | |
126 T& operator*() & { return value(); } | |
127 | |
128 constexpr const T&& operator*() const&& { return std::move(value()); } | |
129 | |
130 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was | |
131 // meant to be 'constexpr const'. | |
132 T&& operator*() && { return std::move(value()); } | |
133 | |
134 constexpr explicit operator bool() const { return !is_null_; } | |
135 | |
136 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was | |
137 // meant to be 'constexpr const'. | |
138 T& value() & { | |
139 DCHECK(!is_null_); | |
140 return *buffer_.template data_as<T>(); | |
141 } | |
142 | |
143 // TODO(mlamouri): can't use 'constexpr' with DCHECK. | |
144 const T& value() const& { | |
145 DCHECK(!is_null_); | |
146 return *buffer_.template data_as<T>(); | |
147 } | |
148 | |
149 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was | |
150 // meant to be 'constexpr const'. | |
151 T&& value() && { | |
152 DCHECK(!is_null_); | |
153 return std::move(*buffer_.template data_as<T>()); | |
154 } | |
155 | |
156 // TODO(mlamouri): can't use 'constexpr' with DCHECK. | |
157 const T&& value() const&& { | |
158 DCHECK(!is_null_); | |
159 return std::move(*buffer_.template data_as<T>()); | |
160 } | |
161 | |
162 template <class U> | |
163 constexpr T value_or(U&& default_value) const& { | |
164 // TODO(mlamouri): add the following assert when possible: | |
165 // static_assert(std::is_copy_constructible<T>::value, | |
166 // "T must be copy constructible"); | |
167 static_assert(std::is_convertible<U, T>::value, | |
168 "U must be convertible to T"); | |
169 return is_null_ ? static_cast<T>(std::forward<U>(default_value)) : value(); | |
170 } | |
171 | |
172 template <class U> | |
173 T value_or(U&& default_value) && { | |
174 // TODO(mlamouri): add the following assert when possible: | |
175 // static_assert(std::is_move_constructible<T>::value, | |
176 // "T must be move constructible"); | |
177 static_assert(std::is_convertible<U, T>::value, | |
178 "U must be convertible to T"); | |
179 return is_null_ ? static_cast<T>(std::forward<U>(default_value)) | |
180 : std::move(value()); | |
181 } | |
182 | |
183 void swap(Optional& other) { | |
184 if (is_null_ && other.is_null_) | |
185 return; | |
186 | |
187 if (is_null_ != other.is_null_) { | |
188 if (is_null_) { | |
189 Init(std::move(*other.buffer_.template data_as<T>())); | |
190 other.FreeIfNeeded(); | |
191 } else { | |
192 other.Init(std::move(*buffer_.template data_as<T>())); | |
193 FreeIfNeeded(); | |
194 } | |
195 return; | |
196 } | |
197 | |
198 DCHECK(!is_null_ && !other.is_null_); | |
199 using std::swap; | |
200 swap(**this, *other); | |
201 } | |
202 | |
203 template <class... Args> | |
204 void emplace(Args&&... args) { | |
205 FreeIfNeeded(); | |
206 Init(std::forward<Args>(args)...); | |
207 } | |
208 | |
209 private: | |
210 void Init(const T& value) { | |
211 DCHECK(is_null_); | |
212 new (buffer_.template data_as<T>()) T(value); | |
213 is_null_ = false; | |
214 } | |
215 | |
216 void Init(T&& value) { | |
217 DCHECK(is_null_); | |
218 new (buffer_.template data_as<T>()) T(std::move(value)); | |
219 is_null_ = false; | |
220 } | |
221 | |
222 template <class... Args> | |
223 void Init(Args&&... args) { | |
224 DCHECK(is_null_); | |
225 new (buffer_.template data_as<T>()) T(std::forward<Args>(args)...); | |
226 is_null_ = false; | |
227 } | |
228 | |
229 void InitOrAssign(const T& value) { | |
230 if (is_null_) | |
231 Init(value); | |
232 else | |
233 *buffer_.template data_as<T>() = value; | |
234 } | |
235 | |
236 void InitOrAssign(T&& value) { | |
237 if (is_null_) | |
238 Init(std::move(value)); | |
239 else | |
240 *buffer_.template data_as<T>() = std::move(value); | |
241 } | |
242 | |
243 void FreeIfNeeded() { | |
244 if (is_null_) | |
245 return; | |
246 buffer_.template data_as<T>()->~T(); | |
247 is_null_ = true; | |
248 } | |
249 | |
250 bool is_null_ = true; | |
251 base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_; | |
252 }; | |
253 | |
254 template <class T> | |
255 constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) { | |
256 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs); | |
257 } | |
258 | |
259 template <class T> | |
260 constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) { | |
261 return !(lhs == rhs); | |
262 } | |
263 | |
264 template <class T> | |
265 constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) { | |
266 return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs); | |
267 } | |
268 | |
269 template <class T> | |
270 constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) { | |
271 return !(rhs < lhs); | |
272 } | |
273 | |
274 template <class T> | |
275 constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) { | |
276 return rhs < lhs; | |
277 } | |
278 | |
279 template <class T> | |
280 constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) { | |
281 return !(lhs < rhs); | |
282 } | |
283 | |
284 template <class T> | |
285 constexpr bool operator==(const Optional<T>& opt, base::nullopt_t) { | |
286 return !opt; | |
287 } | |
288 | |
289 template <class T> | |
290 constexpr bool operator==(base::nullopt_t, const Optional<T>& opt) { | |
291 return !opt; | |
292 } | |
293 | |
294 template <class T> | |
295 constexpr bool operator!=(const Optional<T>& opt, base::nullopt_t) { | |
296 return !!opt; | |
297 } | |
298 | |
299 template <class T> | |
300 constexpr bool operator!=(base::nullopt_t, const Optional<T>& opt) { | |
301 return !!opt; | |
302 } | |
303 | |
304 template <class T> | |
305 constexpr bool operator<(const Optional<T>& opt, base::nullopt_t) { | |
306 return false; | |
307 } | |
308 | |
309 template <class T> | |
310 constexpr bool operator<(base::nullopt_t, const Optional<T>& opt) { | |
311 return !!opt; | |
312 } | |
313 | |
314 template <class T> | |
315 constexpr bool operator<=(const Optional<T>& opt, base::nullopt_t) { | |
316 return !opt; | |
317 } | |
318 | |
319 template <class T> | |
320 constexpr bool operator<=(base::nullopt_t, const Optional<T>& opt) { | |
321 return true; | |
322 } | |
323 | |
324 template <class T> | |
325 constexpr bool operator>(const Optional<T>& opt, base::nullopt_t) { | |
326 return !!opt; | |
327 } | |
328 | |
329 template <class T> | |
330 constexpr bool operator>(base::nullopt_t, const Optional<T>& opt) { | |
331 return false; | |
332 } | |
333 | |
334 template <class T> | |
335 constexpr bool operator>=(const Optional<T>& opt, base::nullopt_t) { | |
336 return true; | |
337 } | |
338 | |
339 template <class T> | |
340 constexpr bool operator>=(base::nullopt_t, const Optional<T>& opt) { | |
341 return !opt; | |
342 } | |
343 | |
344 template <class T> | |
345 constexpr bool operator==(const Optional<T>& opt, const T& value) { | |
346 return opt != nullopt ? *opt == value : false; | |
347 } | |
348 | |
349 template <class T> | |
350 constexpr bool operator==(const T& value, const Optional<T>& opt) { | |
351 return opt == value; | |
352 } | |
353 | |
354 template <class T> | |
355 constexpr bool operator!=(const Optional<T>& opt, const T& value) { | |
356 return !(opt == value); | |
357 } | |
358 | |
359 template <class T> | |
360 constexpr bool operator!=(const T& value, const Optional<T>& opt) { | |
361 return !(opt == value); | |
362 } | |
363 | |
364 template <class T> | |
365 constexpr bool operator<(const Optional<T>& opt, const T& value) { | |
366 return opt != nullopt ? *opt < value : true; | |
367 } | |
368 | |
369 template <class T> | |
370 constexpr bool operator<(const T& value, const Optional<T>& opt) { | |
371 return opt != nullopt ? value < *opt : false; | |
372 } | |
373 | |
374 template <class T> | |
375 constexpr bool operator<=(const Optional<T>& opt, const T& value) { | |
376 return !(opt > value); | |
377 } | |
378 | |
379 template <class T> | |
380 constexpr bool operator<=(const T& value, const Optional<T>& opt) { | |
381 return !(value > opt); | |
382 } | |
383 | |
384 template <class T> | |
385 constexpr bool operator>(const Optional<T>& opt, const T& value) { | |
386 return value < opt; | |
387 } | |
388 | |
389 template <class T> | |
390 constexpr bool operator>(const T& value, const Optional<T>& opt) { | |
391 return opt < value; | |
392 } | |
393 | |
394 template <class T> | |
395 constexpr bool operator>=(const Optional<T>& opt, const T& value) { | |
396 return !(opt < value); | |
397 } | |
398 | |
399 template <class T> | |
400 constexpr bool operator>=(const T& value, const Optional<T>& opt) { | |
401 return !(value < opt); | |
402 } | |
403 | |
404 template <class T> | |
405 constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) { | |
406 return Optional<typename std::decay<T>::type>(std::forward<T>(value)); | |
407 } | |
408 | |
409 template <class T> | |
410 void swap(Optional<T>& lhs, Optional<T>& rhs) { | |
411 lhs.swap(rhs); | |
412 } | |
413 | |
414 } // namespace base | |
415 | |
416 namespace std { | |
417 | |
418 template <class T> | |
419 struct hash<base::Optional<T>> { | |
420 size_t operator()(const base::Optional<T>& opt) const { | |
421 return opt == base::nullopt ? 0 : std::hash<T>()(*opt); | |
422 } | |
423 }; | |
424 | |
425 } // namespace std | |
426 | |
427 #endif // BASE_OPTIONAL_H_ | |
OLD | NEW |