Line data Source code
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 : // This file is a clone of "base/optional.h" in chromium.
6 : // Keep in sync, especially when fixing bugs.
7 : // Copyright 2017 the V8 project authors. All rights reserved.
8 :
9 : #ifndef V8_BASE_OPTIONAL_H_
10 : #define V8_BASE_OPTIONAL_H_
11 :
12 : #include <type_traits>
13 :
14 : #include "src/base/logging.h"
15 :
16 : namespace v8 {
17 : namespace base {
18 :
19 : // Specification:
20 : // http://en.cppreference.com/w/cpp/utility/optional/in_place_t
21 : struct in_place_t {};
22 :
23 : // Specification:
24 : // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
25 : struct nullopt_t {
26 : constexpr explicit nullopt_t(int) {}
27 : };
28 :
29 : // Specification:
30 : // http://en.cppreference.com/w/cpp/utility/optional/in_place
31 : constexpr in_place_t in_place = {};
32 :
33 : // Specification:
34 : // http://en.cppreference.com/w/cpp/utility/optional/nullopt
35 : constexpr nullopt_t nullopt(0);
36 :
37 : namespace internal {
38 :
39 : template <typename T, bool = std::is_trivially_destructible<T>::value>
40 : struct OptionalStorage {
41 : // Initializing |empty_| here instead of using default member initializing
42 : // to avoid errors in g++ 4.8.
43 : constexpr OptionalStorage() : empty_('\0') {}
44 :
45 : constexpr explicit OptionalStorage(const T& value)
46 : : is_null_(false), value_(value) {}
47 :
48 : // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
49 : explicit OptionalStorage(T&& value)
50 : : is_null_(false), value_(std::move(value)) {}
51 :
52 : // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
53 : template <class... Args>
54 : explicit OptionalStorage(base::in_place_t, Args&&... args)
55 : : is_null_(false), value_(std::forward<Args>(args)...) {}
56 :
57 : // When T is not trivially destructible we must call its
58 : // destructor before deallocating its memory.
59 : ~OptionalStorage() {
60 497239 : if (!is_null_) value_.~T();
61 : }
62 :
63 : bool is_null_ = true;
64 : union {
65 : // |empty_| exists so that the union will always be initialized, even when
66 : // it doesn't contain a value. Union members must be initialized for the
67 : // constructor to be 'constexpr'.
68 : char empty_;
69 : T value_;
70 : };
71 : };
72 :
73 : template <typename T>
74 : struct OptionalStorage<T, true> {
75 : // Initializing |empty_| here instead of using default member initializing
76 : // to avoid errors in g++ 4.8.
77 : constexpr OptionalStorage() : empty_('\0') {}
78 :
79 : constexpr explicit OptionalStorage(const T& value)
80 : : is_null_(false), value_(value) {}
81 :
82 : // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
83 : explicit OptionalStorage(T&& value)
84 : : is_null_(false), value_(std::move(value)) {}
85 :
86 : // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
87 : template <class... Args>
88 : explicit OptionalStorage(base::in_place_t, Args&&... args)
89 : : is_null_(false), value_(std::forward<Args>(args)...) {}
90 :
91 : // When T is trivially destructible (i.e. its destructor does nothing) there
92 : // is no need to call it. Explicitly defaulting the destructor means it's not
93 : // user-provided. Those two together make this destructor trivial.
94 : ~OptionalStorage() = default;
95 :
96 : bool is_null_ = true;
97 : union {
98 : // |empty_| exists so that the union will always be initialized, even when
99 : // it doesn't contain a value. Union members must be initialized for the
100 : // constructor to be 'constexpr'.
101 : char empty_;
102 : T value_;
103 : };
104 : };
105 :
106 : } // namespace internal
107 :
108 : // base::Optional is a Chromium version of the C++17 optional class:
109 : // std::optional documentation:
110 : // http://en.cppreference.com/w/cpp/utility/optional
111 : // Chromium documentation:
112 : // https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
113 : //
114 : // These are the differences between the specification and the implementation:
115 : // - The constructor and emplace method using initializer_list are not
116 : // implemented because 'initializer_list' is banned from Chromium.
117 : // - Constructors do not use 'constexpr' as it is a C++14 extension.
118 : // - 'constexpr' might be missing in some places for reasons specified locally.
119 : // - No exceptions are thrown, because they are banned from Chromium.
120 : // - All the non-members are in the 'base' namespace instead of 'std'.
121 : template <typename T>
122 : class Optional {
123 : public:
124 : using value_type = T;
125 :
126 36 : constexpr Optional() {}
127 :
128 : explicit constexpr Optional(base::nullopt_t) {}
129 :
130 2603067 : Optional(const Optional& other) {
131 2603067 : if (!other.storage_.is_null_) Init(other.value());
132 : }
133 :
134 : Optional(Optional&& other) {
135 : if (!other.storage_.is_null_) Init(std::move(other.value()));
136 : }
137 :
138 : explicit constexpr Optional(const T& value) : storage_(value) {}
139 :
140 : // TODO(alshabalin): Can't use 'constexpr' with std::move until C++14.
141 : explicit Optional(T&& value) : storage_(std::move(value)) {}
142 :
143 : // TODO(alshabalin): Can't use 'constexpr' with std::forward until C++14.
144 : template <class... Args>
145 : explicit Optional(base::in_place_t, Args&&... args)
146 : : storage_(base::in_place, std::forward<Args>(args)...) {}
147 :
148 : ~Optional() = default;
149 :
150 : Optional& operator=(base::nullopt_t) {
151 : FreeIfNeeded();
152 : return *this;
153 : }
154 :
155 : Optional& operator=(const Optional& other) {
156 : if (other.storage_.is_null_) {
157 : FreeIfNeeded();
158 : return *this;
159 : }
160 :
161 : InitOrAssign(other.value());
162 : return *this;
163 : }
164 :
165 : Optional& operator=(Optional&& other) {
166 : if (other.storage_.is_null_) {
167 : FreeIfNeeded();
168 : return *this;
169 : }
170 :
171 : InitOrAssign(std::move(other.value()));
172 : return *this;
173 : }
174 :
175 : template <class U>
176 : typename std::enable_if<std::is_same<std::decay<U>, T>::value,
177 : Optional&>::type
178 : operator=(U&& value) {
179 : InitOrAssign(std::forward<U>(value));
180 : return *this;
181 : }
182 :
183 : // TODO(mlamouri): can't use 'constexpr' with DCHECK.
184 : const T* operator->() const {
185 : DCHECK(!storage_.is_null_);
186 : return &value();
187 : }
188 :
189 : // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
190 : // meant to be 'constexpr const'.
191 : T* operator->() {
192 : DCHECK(!storage_.is_null_);
193 : return &value();
194 : }
195 :
196 : constexpr const T& operator*() const & { return value(); }
197 :
198 : // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
199 : // meant to be 'constexpr const'.
200 : T& operator*() & { return value(); }
201 :
202 : constexpr const T&& operator*() const && { return std::move(value()); }
203 :
204 : // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
205 : // meant to be 'constexpr const'.
206 : T&& operator*() && { return std::move(value()); }
207 :
208 : constexpr explicit operator bool() const { return !storage_.is_null_; }
209 :
210 1301515 : constexpr bool has_value() const { return !storage_.is_null_; }
211 :
212 : // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
213 : // meant to be 'constexpr const'.
214 : T& value() & {
215 : DCHECK(!storage_.is_null_);
216 : return storage_.value_;
217 : }
218 :
219 : // TODO(mlamouri): can't use 'constexpr' with DCHECK.
220 : const T& value() const & {
221 : DCHECK(!storage_.is_null_);
222 : return storage_.value_;
223 : }
224 :
225 : // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
226 : // meant to be 'constexpr const'.
227 : T&& value() && {
228 : DCHECK(!storage_.is_null_);
229 : return std::move(storage_.value_);
230 : }
231 :
232 : // TODO(mlamouri): can't use 'constexpr' with DCHECK.
233 : const T&& value() const && {
234 : DCHECK(!storage_.is_null_);
235 : return std::move(storage_.value_);
236 : }
237 :
238 : template <class U>
239 : constexpr T value_or(U&& default_value) const & {
240 : // TODO(mlamouri): add the following assert when possible:
241 : // static_assert(std::is_copy_constructible<T>::value,
242 : // "T must be copy constructible");
243 : static_assert(std::is_convertible<U, T>::value,
244 : "U must be convertible to T");
245 : return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
246 : : value();
247 : }
248 :
249 : template <class U>
250 : T value_or(U&& default_value) && {
251 : // TODO(mlamouri): add the following assert when possible:
252 : // static_assert(std::is_move_constructible<T>::value,
253 : // "T must be move constructible");
254 : static_assert(std::is_convertible<U, T>::value,
255 : "U must be convertible to T");
256 : return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
257 : : std::move(value());
258 : }
259 :
260 : void swap(Optional& other) {
261 : if (storage_.is_null_ && other.storage_.is_null_) return;
262 :
263 : if (storage_.is_null_ != other.storage_.is_null_) {
264 : if (storage_.is_null_) {
265 : Init(std::move(other.storage_.value_));
266 : other.FreeIfNeeded();
267 : } else {
268 : other.Init(std::move(storage_.value_));
269 : FreeIfNeeded();
270 : }
271 : return;
272 : }
273 :
274 : DCHECK(!storage_.is_null_ && !other.storage_.is_null_);
275 : using std::swap;
276 : swap(**this, *other);
277 : }
278 :
279 : void reset() { FreeIfNeeded(); }
280 :
281 : template <class... Args>
282 18922 : void emplace(Args&&... args) {
283 : FreeIfNeeded();
284 : Init(std::forward<Args>(args)...);
285 18922 : }
286 :
287 : private:
288 : void Init(const T& value) {
289 : DCHECK(storage_.is_null_);
290 11618 : new (&storage_.value_) T(value);
291 11618 : storage_.is_null_ = false;
292 : }
293 :
294 : void Init(T&& value) {
295 : DCHECK(storage_.is_null_);
296 : new (&storage_.value_) T(std::move(value));
297 : storage_.is_null_ = false;
298 : }
299 :
300 : template <class... Args>
301 : void Init(Args&&... args) {
302 : DCHECK(storage_.is_null_);
303 18922 : new (&storage_.value_) T(std::forward<Args>(args)...);
304 18922 : storage_.is_null_ = false;
305 : }
306 :
307 : void InitOrAssign(const T& value) {
308 : if (storage_.is_null_)
309 : Init(value);
310 : else
311 : storage_.value_ = value;
312 : }
313 :
314 : void InitOrAssign(T&& value) {
315 : if (storage_.is_null_)
316 : Init(std::move(value));
317 : else
318 : storage_.value_ = std::move(value);
319 : }
320 :
321 : void FreeIfNeeded() {
322 18922 : if (storage_.is_null_) return;
323 0 : storage_.value_.~T();
324 0 : storage_.is_null_ = true;
325 : }
326 :
327 : internal::OptionalStorage<T> storage_;
328 : };
329 :
330 : template <class T>
331 : constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
332 : return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
333 : }
334 :
335 : template <class T>
336 : constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) {
337 : return !(lhs == rhs);
338 : }
339 :
340 : template <class T>
341 : constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) {
342 : return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs);
343 : }
344 :
345 : template <class T>
346 : constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) {
347 : return !(rhs < lhs);
348 : }
349 :
350 : template <class T>
351 : constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) {
352 : return rhs < lhs;
353 : }
354 :
355 : template <class T>
356 : constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) {
357 : return !(lhs < rhs);
358 : }
359 :
360 : template <class T>
361 : constexpr bool operator==(const Optional<T>& opt, base::nullopt_t) {
362 : return !opt;
363 : }
364 :
365 : template <class T>
366 : constexpr bool operator==(base::nullopt_t, const Optional<T>& opt) {
367 : return !opt;
368 : }
369 :
370 : template <class T>
371 : constexpr bool operator!=(const Optional<T>& opt, base::nullopt_t) {
372 : return !!opt;
373 : }
374 :
375 : template <class T>
376 : constexpr bool operator!=(base::nullopt_t, const Optional<T>& opt) {
377 : return !!opt;
378 : }
379 :
380 : template <class T>
381 : constexpr bool operator<(const Optional<T>& opt, base::nullopt_t) {
382 : return false;
383 : }
384 :
385 : template <class T>
386 : constexpr bool operator<(base::nullopt_t, const Optional<T>& opt) {
387 : return !!opt;
388 : }
389 :
390 : template <class T>
391 : constexpr bool operator<=(const Optional<T>& opt, base::nullopt_t) {
392 : return !opt;
393 : }
394 :
395 : template <class T>
396 : constexpr bool operator<=(base::nullopt_t, const Optional<T>& opt) {
397 : return true;
398 : }
399 :
400 : template <class T>
401 : constexpr bool operator>(const Optional<T>& opt, base::nullopt_t) {
402 : return !!opt;
403 : }
404 :
405 : template <class T>
406 : constexpr bool operator>(base::nullopt_t, const Optional<T>& opt) {
407 : return false;
408 : }
409 :
410 : template <class T>
411 : constexpr bool operator>=(const Optional<T>& opt, base::nullopt_t) {
412 : return true;
413 : }
414 :
415 : template <class T>
416 : constexpr bool operator>=(base::nullopt_t, const Optional<T>& opt) {
417 : return !opt;
418 : }
419 :
420 : template <class T>
421 : constexpr bool operator==(const Optional<T>& opt, const T& value) {
422 : return opt != nullopt ? *opt == value : false;
423 : }
424 :
425 : template <class T>
426 : constexpr bool operator==(const T& value, const Optional<T>& opt) {
427 : return opt == value;
428 : }
429 :
430 : template <class T>
431 : constexpr bool operator!=(const Optional<T>& opt, const T& value) {
432 : return !(opt == value);
433 : }
434 :
435 : template <class T>
436 : constexpr bool operator!=(const T& value, const Optional<T>& opt) {
437 : return !(opt == value);
438 : }
439 :
440 : template <class T>
441 : constexpr bool operator<(const Optional<T>& opt, const T& value) {
442 : return opt != nullopt ? *opt < value : true;
443 : }
444 :
445 : template <class T>
446 : constexpr bool operator<(const T& value, const Optional<T>& opt) {
447 : return opt != nullopt ? value < *opt : false;
448 : }
449 :
450 : template <class T>
451 : constexpr bool operator<=(const Optional<T>& opt, const T& value) {
452 : return !(opt > value);
453 : }
454 :
455 : template <class T>
456 : constexpr bool operator<=(const T& value, const Optional<T>& opt) {
457 : return !(value > opt);
458 : }
459 :
460 : template <class T>
461 : constexpr bool operator>(const Optional<T>& opt, const T& value) {
462 : return value < opt;
463 : }
464 :
465 : template <class T>
466 : constexpr bool operator>(const T& value, const Optional<T>& opt) {
467 : return opt < value;
468 : }
469 :
470 : template <class T>
471 : constexpr bool operator>=(const Optional<T>& opt, const T& value) {
472 : return !(opt < value);
473 : }
474 :
475 : template <class T>
476 : constexpr bool operator>=(const T& value, const Optional<T>& opt) {
477 : return !(value < opt);
478 : }
479 :
480 : template <class T>
481 : constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
482 : return Optional<typename std::decay<T>::type>(std::forward<T>(value));
483 : }
484 :
485 : template <class T>
486 : void swap(Optional<T>& lhs, Optional<T>& rhs) {
487 : lhs.swap(rhs);
488 : }
489 :
490 : } // namespace base
491 : } // namespace v8
492 :
493 : #endif // V8_BASE_OPTIONAL_H_
|