Bug 1684463 - [devtools] Part 1: Shorten the _createAttribute function by refactoring...
[gecko.git] / mfbt / Maybe.h
blob1f650b3527bfaa8e07ac62b69c155205d76b80fb
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* A class for optional values and in-place lazy construction. */
9 #ifndef mozilla_Maybe_h
10 #define mozilla_Maybe_h
12 #include <new> // for placement new
13 #include <ostream>
14 #include <type_traits>
15 #include <utility>
17 #include "mozilla/Alignment.h"
18 #include "mozilla/Assertions.h"
19 #include "mozilla/Attributes.h"
20 #include "mozilla/MemoryChecking.h"
21 #include "mozilla/OperatorNewExtensions.h"
22 #include "mozilla/Poison.h"
24 class nsCycleCollectionTraversalCallback;
26 template <typename T>
27 inline void CycleCollectionNoteChild(
28 nsCycleCollectionTraversalCallback& aCallback, T* aChild, const char* aName,
29 uint32_t aFlags);
31 namespace mozilla {
33 struct Nothing {};
35 inline constexpr bool operator==(const Nothing&, const Nothing&) {
36 return true;
39 template <class T>
40 class Maybe;
42 namespace detail {
44 // You would think that poisoning Maybe instances could just be a call
45 // to mozWritePoison. Unfortunately, using a simple call to
46 // mozWritePoison generates poor code on MSVC for small structures. The
47 // generated code contains (always not-taken) branches and does a bunch
48 // of setup for `rep stos{l,q}`, even though we know at compile time
49 // exactly how many words we're poisoning. Instead, we're going to
50 // force MSVC to generate the code we want via recursive templates.
52 // Write the given poisonValue into p at offset*sizeof(uintptr_t).
53 template <size_t offset>
54 inline void WritePoisonAtOffset(void* p, const uintptr_t poisonValue) {
55 memcpy(static_cast<char*>(p) + offset * sizeof(poisonValue), &poisonValue,
56 sizeof(poisonValue));
59 template <size_t Offset, size_t NOffsets>
60 struct InlinePoisoner {
61 static void poison(void* p, const uintptr_t poisonValue) {
62 WritePoisonAtOffset<Offset>(p, poisonValue);
63 InlinePoisoner<Offset + 1, NOffsets>::poison(p, poisonValue);
67 template <size_t N>
68 struct InlinePoisoner<N, N> {
69 static void poison(void*, const uintptr_t) {
70 // All done!
74 // We can't generate inline code for large structures, though, because we'll
75 // blow out recursive template instantiation limits, and the code would be
76 // bloated to boot. So provide a fallback to the out-of-line poisoner.
77 template <size_t ObjectSize>
78 struct OutOfLinePoisoner {
79 static MOZ_NEVER_INLINE void poison(void* p, const uintptr_t) {
80 mozWritePoison(p, ObjectSize);
84 template <typename T>
85 inline void PoisonObject(T* p) {
86 const uintptr_t POISON = mozPoisonValue();
87 std::conditional_t<(sizeof(T) <= 8 * sizeof(POISON)),
88 InlinePoisoner<0, sizeof(T) / sizeof(POISON)>,
89 OutOfLinePoisoner<sizeof(T)>>::poison(p, POISON);
92 template <typename T>
93 struct MaybePoisoner {
94 static const size_t N = sizeof(T);
96 static void poison(void* aPtr) {
97 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
98 if (N >= sizeof(uintptr_t)) {
99 PoisonObject(static_cast<std::remove_cv_t<T>*>(aPtr));
101 #endif
102 MOZ_MAKE_MEM_UNDEFINED(aPtr, N);
106 template <typename T>
107 constexpr bool IsTriviallyDestructibleAndCopyable =
108 std::is_trivially_destructible_v<T> &&
109 (std::is_trivially_copy_constructible_v<T> ||
110 !std::is_copy_constructible_v<T>);
112 template <typename T,
113 bool TriviallyDestructibleAndCopyable =
114 IsTriviallyDestructibleAndCopyable<T>,
115 bool Copyable = std::is_copy_constructible_v<T>,
116 bool Movable = std::is_move_constructible_v<T>>
117 class Maybe_CopyMove_Enabler;
119 #define MOZ_MAYBE_COPY_OPS() \
120 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler& aOther) { \
121 if (downcast(aOther).isSome()) { \
122 downcast(*this).emplace(*downcast(aOther)); \
126 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler& aOther) { \
127 return downcast(*this).template operator=<T>(downcast(aOther)); \
130 #define MOZ_MAYBE_MOVE_OPS() \
131 constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) { \
132 if (downcast(aOther).isSome()) { \
133 downcast(*this).emplace(std::move(*downcast(aOther))); \
134 downcast(aOther).reset(); \
138 constexpr Maybe_CopyMove_Enabler& operator=( \
139 Maybe_CopyMove_Enabler&& aOther) { \
140 downcast(*this).template operator=<T>(std::move(downcast(aOther))); \
142 return *this; \
145 #define MOZ_MAYBE_DOWNCAST() \
146 static constexpr Maybe<T>& downcast(Maybe_CopyMove_Enabler& aObj) { \
147 return static_cast<Maybe<T>&>(aObj); \
149 static constexpr const Maybe<T>& downcast( \
150 const Maybe_CopyMove_Enabler& aObj) { \
151 return static_cast<const Maybe<T>&>(aObj); \
154 template <typename T>
155 class Maybe_CopyMove_Enabler<T, true, true, true> {
156 public:
157 Maybe_CopyMove_Enabler() = default;
159 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = default;
160 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = default;
161 constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) {
162 downcast(aOther).reset();
164 constexpr Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&& aOther) {
165 downcast(aOther).reset();
166 return *this;
169 private:
170 MOZ_MAYBE_DOWNCAST()
173 template <typename T>
174 class Maybe_CopyMove_Enabler<T, true, false, true> {
175 public:
176 Maybe_CopyMove_Enabler() = default;
178 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete;
179 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete;
180 constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) {
181 downcast(aOther).reset();
183 constexpr Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&& aOther) {
184 downcast(aOther).reset();
185 return *this;
188 private:
189 MOZ_MAYBE_DOWNCAST()
192 template <typename T>
193 class Maybe_CopyMove_Enabler<T, false, true, true> {
194 public:
195 Maybe_CopyMove_Enabler() = default;
197 MOZ_MAYBE_COPY_OPS()
198 MOZ_MAYBE_MOVE_OPS()
200 private:
201 MOZ_MAYBE_DOWNCAST()
204 template <typename T>
205 class Maybe_CopyMove_Enabler<T, false, false, true> {
206 public:
207 Maybe_CopyMove_Enabler() = default;
209 MOZ_MAYBE_MOVE_OPS()
211 private:
212 MOZ_MAYBE_DOWNCAST()
215 template <typename T>
216 class Maybe_CopyMove_Enabler<T, false, true, false> {
217 public:
218 Maybe_CopyMove_Enabler() = default;
220 MOZ_MAYBE_COPY_OPS()
222 private:
223 MOZ_MAYBE_DOWNCAST()
226 template <typename T, bool TriviallyDestructibleAndCopyable>
227 class Maybe_CopyMove_Enabler<T, TriviallyDestructibleAndCopyable, false,
228 false> {
229 public:
230 Maybe_CopyMove_Enabler() = default;
232 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete;
233 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete;
234 Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&&) = delete;
235 Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&&) = delete;
238 #undef MOZ_MAYBE_COPY_OPS
239 #undef MOZ_MAYBE_MOVE_OPS
240 #undef MOZ_MAYBE_DOWNCAST
242 template <typename T, bool TriviallyDestructibleAndCopyable =
243 IsTriviallyDestructibleAndCopyable<T>>
244 struct MaybeStorage;
246 template <typename T>
247 struct MaybeStorage<T, false> {
248 using NonConstT = std::remove_const_t<T>;
250 union Union {
251 Union() {}
252 constexpr explicit Union(const T& aVal) : val{aVal} {}
253 template <typename U,
254 typename = std::enable_if_t<std::is_move_constructible_v<U>>>
255 constexpr explicit Union(U&& aVal) : val{std::forward<U>(aVal)} {}
257 ~Union() {}
259 NonConstT val;
260 char dummy;
261 } mStorage;
262 char mIsSome = false; // not bool -- guarantees minimal space consumption
264 MaybeStorage() = default;
265 explicit MaybeStorage(const T& aVal) : mStorage{aVal}, mIsSome{true} {}
266 explicit MaybeStorage(T&& aVal) : mStorage{std::move(aVal)}, mIsSome{true} {}
268 template <typename... Args>
269 explicit MaybeStorage(std::in_place_t, Args&&... aArgs) : mIsSome{true} {
270 ::new (KnownNotNull, &mStorage.val) T(std::forward<Args>(aArgs)...);
273 // Copy and move operations are no-ops, since copying is moving is implemented
274 // by Maybe_CopyMove_Enabler.
276 MaybeStorage(const MaybeStorage&) {}
277 MaybeStorage& operator=(const MaybeStorage&) { return *this; }
278 MaybeStorage(MaybeStorage&&) {}
279 MaybeStorage& operator=(MaybeStorage&&) { return *this; }
281 ~MaybeStorage() {
282 if (mIsSome) {
283 mStorage.val.T::~T();
288 template <typename T>
289 struct MaybeStorage<T, true> {
290 using NonConstT = std::remove_const_t<T>;
292 union Union {
293 constexpr Union() : dummy() {}
294 constexpr explicit Union(const T& aVal) : val{aVal} {}
295 constexpr explicit Union(T&& aVal) : val{std::move(aVal)} {}
296 template <typename... Args>
297 constexpr explicit Union(std::in_place_t, Args&&... aArgs)
298 : val{std::forward<Args>(aArgs)...} {}
300 NonConstT val;
301 char dummy;
302 } mStorage;
303 char mIsSome = false; // not bool -- guarantees minimal space consumption
305 constexpr MaybeStorage() = default;
306 constexpr explicit MaybeStorage(const T& aVal)
307 : mStorage{aVal}, mIsSome{true} {}
308 constexpr explicit MaybeStorage(T&& aVal)
309 : mStorage{std::move(aVal)}, mIsSome{true} {}
311 template <typename... Args>
312 constexpr explicit MaybeStorage(std::in_place_t, Args&&... aArgs)
313 : mStorage{std::in_place, std::forward<Args>(aArgs)...}, mIsSome{true} {}
316 } // namespace detail
318 template <typename T, typename U = typename std::remove_cv<
319 typename std::remove_reference<T>::type>::type>
320 constexpr Maybe<U> Some(T&& aValue);
323 * Maybe is a container class which contains either zero or one elements. It
324 * serves two roles. It can represent values which are *semantically* optional,
325 * augmenting a type with an explicit 'Nothing' value. In this role, it provides
326 * methods that make it easy to work with values that may be missing, along with
327 * equality and comparison operators so that Maybe values can be stored in
328 * containers. Maybe values can be constructed conveniently in expressions using
329 * type inference, as follows:
331 * void doSomething(Maybe<Foo> aFoo) {
332 * if (aFoo) // Make sure that aFoo contains a value...
333 * aFoo->takeAction(); // and then use |aFoo->| to access it.
334 * } // |*aFoo| also works!
336 * doSomething(Nothing()); // Passes a Maybe<Foo> containing no value.
337 * doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|.
339 * You'll note that it's important to check whether a Maybe contains a value
340 * before using it, using conversion to bool, |isSome()|, or |isNothing()|. You
341 * can avoid these checks, and sometimes write more readable code, using
342 * |valueOr()|, |ptrOr()|, and |refOr()|, which allow you to retrieve the value
343 * in the Maybe and provide a default for the 'Nothing' case. You can also use
344 * |apply()| to call a function only if the Maybe holds a value, and |map()| to
345 * transform the value in the Maybe, returning another Maybe with a possibly
346 * different type.
348 * Maybe's other role is to support lazily constructing objects without using
349 * dynamic storage. A Maybe directly contains storage for a value, but it's
350 * empty by default. |emplace()|, as mentioned above, can be used to construct a
351 * value in Maybe's storage. The value a Maybe contains can be destroyed by
352 * calling |reset()|; this will happen automatically if a Maybe is destroyed
353 * while holding a value.
355 * It's a common idiom in C++ to use a pointer as a 'Maybe' type, with a null
356 * value meaning 'Nothing' and any other value meaning 'Some'. You can convert
357 * from such a pointer to a Maybe value using 'ToMaybe()'.
359 * Maybe is inspired by similar types in the standard library of many other
360 * languages (e.g. Haskell's Maybe and Rust's Option). In the C++ world it's
361 * very similar to std::optional, which was proposed for C++14 and originated in
362 * Boost. The most important differences between Maybe and std::optional are:
364 * - std::optional<T> may be compared with T. We deliberately forbid that.
365 * - std::optional has |valueOr()|, equivalent to Maybe's |valueOr()|, but
366 * lacks corresponding methods for |refOr()| and |ptrOr()|.
367 * - std::optional lacks |map()| and |apply()|, making it less suitable for
368 * functional-style code.
369 * - std::optional lacks many convenience functions that Maybe has. Most
370 * unfortunately, it lacks equivalents of the type-inferred constructor
371 * functions |Some()| and |Nothing()|.
373 template <class T>
374 class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe
375 : private detail::MaybeStorage<T>,
376 public detail::Maybe_CopyMove_Enabler<T> {
377 template <typename, bool, bool, bool>
378 friend class detail::Maybe_CopyMove_Enabler;
380 template <typename U, typename V>
381 friend constexpr Maybe<V> Some(U&& aValue);
383 struct SomeGuard {};
385 template <typename U>
386 constexpr Maybe(U&& aValue, SomeGuard)
387 : detail::MaybeStorage<T>{std::forward<U>(aValue)} {}
389 using detail::MaybeStorage<T>::mIsSome;
390 using detail::MaybeStorage<T>::mStorage;
392 void poisonData() { detail::MaybePoisoner<T>::poison(&mStorage.val); }
394 public:
395 using ValueType = T;
397 MOZ_ALLOW_TEMPORARY constexpr Maybe() = default;
399 MOZ_ALLOW_TEMPORARY MOZ_IMPLICIT constexpr Maybe(Nothing) : Maybe{} {}
401 template <typename... Args>
402 constexpr explicit Maybe(std::in_place_t, Args&&... aArgs)
403 : detail::MaybeStorage<T>{std::in_place, std::forward<Args>(aArgs)...} {}
406 * Maybe<T> can be copy-constructed from a Maybe<U> if T is constructible from
407 * a const U&.
409 template <typename U,
410 typename = std::enable_if_t<std::is_constructible_v<T, const U&>>>
411 MOZ_IMPLICIT Maybe(const Maybe<U>& aOther) {
412 if (aOther.isSome()) {
413 emplace(*aOther);
418 * Maybe<T> can be move-constructed from a Maybe<U> if T is constructible from
419 * a U&&.
421 template <typename U,
422 typename = std::enable_if_t<std::is_constructible_v<T, U&&>>>
423 MOZ_IMPLICIT Maybe(Maybe<U>&& aOther) {
424 if (aOther.isSome()) {
425 emplace(std::move(*aOther));
426 aOther.reset();
430 template <typename U,
431 typename = std::enable_if_t<std::is_constructible_v<T, const U&>>>
432 Maybe& operator=(const Maybe<U>& aOther) {
433 if (aOther.isSome()) {
434 if (mIsSome) {
435 ref() = aOther.ref();
436 } else {
437 emplace(*aOther);
439 } else {
440 reset();
442 return *this;
445 template <typename U,
446 typename = std::enable_if_t<std::is_constructible_v<T, U&&>>>
447 Maybe& operator=(Maybe<U>&& aOther) {
448 if (aOther.isSome()) {
449 if (mIsSome) {
450 ref() = std::move(aOther.ref());
451 } else {
452 emplace(std::move(*aOther));
454 aOther.reset();
455 } else {
456 reset();
459 return *this;
462 constexpr Maybe& operator=(Nothing) {
463 reset();
464 return *this;
467 /* Methods that check whether this Maybe contains a value */
468 constexpr explicit operator bool() const { return isSome(); }
469 constexpr bool isSome() const { return mIsSome; }
470 constexpr bool isNothing() const { return !mIsSome; }
472 /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|.
474 constexpr T value() const&;
475 constexpr T value() &&;
476 constexpr T value() const&&;
479 * Move the contents of this Maybe<T> out of internal storage and return it
480 * without calling the destructor. The internal storage is also reset to
481 * avoid multiple calls. Unsafe unless |isSome()|.
483 T extract() {
484 MOZ_DIAGNOSTIC_ASSERT(isSome());
485 T v = std::move(mStorage.val);
486 reset();
487 return v;
491 * Returns the value (possibly |Nothing()|) by moving it out of this Maybe<T>
492 * and leaving |Nothing()| in its place.
494 Maybe<T> take() { return std::exchange(*this, Nothing()); }
497 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
498 * the default value provided.
500 * Note: If the value passed to aDefault is not the result of a trivial
501 * expression, but expensive to evaluate, e.g. |valueOr(ExpensiveFunction())|,
502 * use |valueOrFrom| instead, e.g.
503 * |valueOrFrom([arg] { return ExpensiveFunction(arg); })|. This ensures
504 * that the expensive expression is only evaluated when its result will
505 * actually be used.
507 template <typename V>
508 constexpr T valueOr(V&& aDefault) const {
509 if (isSome()) {
510 return ref();
512 return std::forward<V>(aDefault);
516 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
517 * the value returned from the function or functor provided.
519 template <typename F>
520 constexpr T valueOrFrom(F&& aFunc) const {
521 if (isSome()) {
522 return ref();
524 return aFunc();
527 /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|.
529 T* ptr();
530 constexpr const T* ptr() const;
533 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
534 * returns the default value provided.
536 T* ptrOr(T* aDefault) {
537 if (isSome()) {
538 return ptr();
540 return aDefault;
543 constexpr const T* ptrOr(const T* aDefault) const {
544 if (isSome()) {
545 return ptr();
547 return aDefault;
551 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
552 * returns the value returned from the function or functor provided.
554 template <typename F>
555 T* ptrOrFrom(F&& aFunc) {
556 if (isSome()) {
557 return ptr();
559 return aFunc();
562 template <typename F>
563 const T* ptrOrFrom(F&& aFunc) const {
564 if (isSome()) {
565 return ptr();
567 return aFunc();
570 constexpr T* operator->();
571 constexpr const T* operator->() const;
573 /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
574 constexpr T& ref() &;
575 constexpr const T& ref() const&;
576 constexpr T&& ref() &&;
577 constexpr const T&& ref() const&&;
580 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
581 * the default value provided.
583 constexpr T& refOr(T& aDefault) {
584 if (isSome()) {
585 return ref();
587 return aDefault;
590 constexpr const T& refOr(const T& aDefault) const {
591 if (isSome()) {
592 return ref();
594 return aDefault;
598 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
599 * value returned from the function or functor provided.
601 template <typename F>
602 constexpr T& refOrFrom(F&& aFunc) {
603 if (isSome()) {
604 return ref();
606 return aFunc();
609 template <typename F>
610 constexpr const T& refOrFrom(F&& aFunc) const {
611 if (isSome()) {
612 return ref();
614 return aFunc();
617 constexpr T& operator*() &;
618 constexpr const T& operator*() const&;
619 constexpr T&& operator*() &&;
620 constexpr const T&& operator*() const&&;
622 /* If |isSome()|, runs the provided function or functor on the contents of
623 * this Maybe. */
624 template <typename Func>
625 constexpr Maybe& apply(Func&& aFunc) {
626 if (isSome()) {
627 std::forward<Func>(aFunc)(ref());
629 return *this;
632 template <typename Func>
633 constexpr const Maybe& apply(Func&& aFunc) const {
634 if (isSome()) {
635 std::forward<Func>(aFunc)(ref());
637 return *this;
641 * If |isSome()|, runs the provided function and returns the result wrapped
642 * in a Maybe. If |isNothing()|, returns an empty Maybe value with the same
643 * value type as what the provided function would have returned.
645 template <typename Func>
646 constexpr auto map(Func&& aFunc) {
647 if (isSome()) {
648 return Some(std::forward<Func>(aFunc)(ref()));
650 return Maybe<decltype(std::forward<Func>(aFunc)(ref()))>{};
653 template <typename Func>
654 constexpr auto map(Func&& aFunc) const {
655 if (isSome()) {
656 return Some(std::forward<Func>(aFunc)(ref()));
658 return Maybe<decltype(std::forward<Func>(aFunc)(ref()))>{};
661 /* If |isSome()|, empties this Maybe and destroys its contents. */
662 constexpr void reset() {
663 if (isSome()) {
664 if constexpr (!std::is_trivially_destructible_v<T>) {
665 ref().T::~T();
666 poisonData();
668 mIsSome = false;
673 * Constructs a T value in-place in this empty Maybe<T>'s storage. The
674 * arguments to |emplace()| are the parameters to T's constructor.
676 template <typename... Args>
677 constexpr void emplace(Args&&... aArgs);
679 template <typename U>
680 constexpr std::enable_if_t<std::is_same_v<T, U> &&
681 std::is_copy_constructible_v<U> &&
682 !std::is_move_constructible_v<U>>
683 emplace(U&& aArgs) {
684 emplace(aArgs);
687 friend std::ostream& operator<<(std::ostream& aStream,
688 const Maybe<T>& aMaybe) {
689 if (aMaybe) {
690 aStream << aMaybe.ref();
691 } else {
692 aStream << "<Nothing>";
694 return aStream;
698 template <typename T>
699 class Maybe<T&> {
700 public:
701 constexpr Maybe() = default;
702 constexpr MOZ_IMPLICIT Maybe(Nothing) {}
704 void emplace(T& aRef) { mValue = &aRef; }
706 /* Methods that check whether this Maybe contains a value */
707 constexpr explicit operator bool() const { return isSome(); }
708 constexpr bool isSome() const { return mValue; }
709 constexpr bool isNothing() const { return !mValue; }
711 T& ref() const {
712 MOZ_DIAGNOSTIC_ASSERT(isSome());
713 return *mValue;
716 T* operator->() const { return &ref(); }
717 T& operator*() const { return ref(); }
719 // Deliberately not defining value and ptr accessors, as these may be
720 // confusing on a reference-typed Maybe.
722 // XXX Should we define refOr?
724 void reset() { mValue = nullptr; }
726 template <typename Func>
727 Maybe& apply(Func&& aFunc) {
728 if (isSome()) {
729 std::forward<Func>(aFunc)(ref());
731 return *this;
734 template <typename Func>
735 const Maybe& apply(Func&& aFunc) const {
736 if (isSome()) {
737 std::forward<Func>(aFunc)(ref());
739 return *this;
742 template <typename Func>
743 auto map(Func&& aFunc) {
744 Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val;
745 if (isSome()) {
746 val.emplace(std::forward<Func>(aFunc)(ref()));
748 return val;
751 template <typename Func>
752 auto map(Func&& aFunc) const {
753 Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val;
754 if (isSome()) {
755 val.emplace(std::forward<Func>(aFunc)(ref()));
757 return val;
760 bool refEquals(const Maybe<T&>& aOther) const {
761 return mValue == aOther.mValue;
764 bool refEquals(const T& aOther) const { return mValue == &aOther; }
766 private:
767 T* mValue = nullptr;
770 template <typename T>
771 constexpr T Maybe<T>::value() const& {
772 MOZ_DIAGNOSTIC_ASSERT(isSome());
773 return ref();
776 template <typename T>
777 constexpr T Maybe<T>::value() && {
778 MOZ_DIAGNOSTIC_ASSERT(isSome());
779 return std::move(ref());
782 template <typename T>
783 constexpr T Maybe<T>::value() const&& {
784 MOZ_DIAGNOSTIC_ASSERT(isSome());
785 return std::move(ref());
788 template <typename T>
789 T* Maybe<T>::ptr() {
790 MOZ_DIAGNOSTIC_ASSERT(isSome());
791 return &ref();
794 template <typename T>
795 constexpr const T* Maybe<T>::ptr() const {
796 MOZ_DIAGNOSTIC_ASSERT(isSome());
797 return &ref();
800 template <typename T>
801 constexpr T* Maybe<T>::operator->() {
802 MOZ_DIAGNOSTIC_ASSERT(isSome());
803 return ptr();
806 template <typename T>
807 constexpr const T* Maybe<T>::operator->() const {
808 MOZ_DIAGNOSTIC_ASSERT(isSome());
809 return ptr();
812 template <typename T>
813 constexpr T& Maybe<T>::ref() & {
814 MOZ_DIAGNOSTIC_ASSERT(isSome());
815 return mStorage.val;
818 template <typename T>
819 constexpr const T& Maybe<T>::ref() const& {
820 MOZ_DIAGNOSTIC_ASSERT(isSome());
821 return mStorage.val;
824 template <typename T>
825 constexpr T&& Maybe<T>::ref() && {
826 MOZ_DIAGNOSTIC_ASSERT(isSome());
827 return std::move(mStorage.val);
830 template <typename T>
831 constexpr const T&& Maybe<T>::ref() const&& {
832 MOZ_DIAGNOSTIC_ASSERT(isSome());
833 return std::move(mStorage.val);
836 template <typename T>
837 constexpr T& Maybe<T>::operator*() & {
838 MOZ_DIAGNOSTIC_ASSERT(isSome());
839 return ref();
842 template <typename T>
843 constexpr const T& Maybe<T>::operator*() const& {
844 MOZ_DIAGNOSTIC_ASSERT(isSome());
845 return ref();
848 template <typename T>
849 constexpr T&& Maybe<T>::operator*() && {
850 MOZ_DIAGNOSTIC_ASSERT(isSome());
851 return std::move(ref());
854 template <typename T>
855 constexpr const T&& Maybe<T>::operator*() const&& {
856 MOZ_DIAGNOSTIC_ASSERT(isSome());
857 return std::move(ref());
860 template <typename T>
861 template <typename... Args>
862 constexpr void Maybe<T>::emplace(Args&&... aArgs) {
863 MOZ_DIAGNOSTIC_ASSERT(!isSome());
864 ::new (KnownNotNull, &mStorage.val) T(std::forward<Args>(aArgs)...);
865 mIsSome = true;
869 * Some() creates a Maybe<T> value containing the provided T value. If T has a
870 * move constructor, it's used to make this as efficient as possible.
872 * Some() selects the type of Maybe it returns by removing any const, volatile,
873 * or reference qualifiers from the type of the value you pass to it. This gives
874 * it more intuitive behavior when used in expressions, but it also means that
875 * if you need to construct a Maybe value that holds a const, volatile, or
876 * reference value, you need to use emplace() instead.
878 template <typename T, typename U>
879 constexpr Maybe<U> Some(T&& aValue) {
880 return {std::forward<T>(aValue), typename Maybe<U>::SomeGuard{}};
883 template <typename T>
884 constexpr Maybe<T&> SomeRef(T& aValue) {
885 Maybe<T&> value;
886 value.emplace(aValue);
887 return value;
890 template <typename T>
891 constexpr Maybe<T&> ToMaybeRef(T* const aPtr) {
892 return aPtr ? SomeRef(*aPtr) : Nothing{};
895 template <typename T>
896 Maybe<std::remove_cv_t<std::remove_reference_t<T>>> ToMaybe(T* aPtr) {
897 if (aPtr) {
898 return Some(*aPtr);
900 return Nothing();
904 * Two Maybe<T> values are equal if
905 * - both are Nothing, or
906 * - both are Some, and the values they contain are equal.
908 template <typename T>
909 constexpr bool operator==(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
910 static_assert(!std::is_reference_v<T>,
911 "operator== is not defined for Maybe<T&>, compare values or "
912 "addresses explicitly instead");
913 if (aLHS.isNothing() != aRHS.isNothing()) {
914 return false;
916 return aLHS.isNothing() || *aLHS == *aRHS;
919 template <typename T>
920 constexpr bool operator!=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
921 return !(aLHS == aRHS);
925 * We support comparison to Nothing to allow reasonable expressions like:
926 * if (maybeValue == Nothing()) { ... }
928 template <typename T>
929 constexpr bool operator==(const Maybe<T>& aLHS, const Nothing& aRHS) {
930 return aLHS.isNothing();
933 template <typename T>
934 constexpr bool operator!=(const Maybe<T>& aLHS, const Nothing& aRHS) {
935 return !(aLHS == aRHS);
938 template <typename T>
939 constexpr bool operator==(const Nothing& aLHS, const Maybe<T>& aRHS) {
940 return aRHS.isNothing();
943 template <typename T>
944 constexpr bool operator!=(const Nothing& aLHS, const Maybe<T>& aRHS) {
945 return !(aLHS == aRHS);
949 * Maybe<T> values are ordered in the same way T values are ordered, except that
950 * Nothing comes before anything else.
952 template <typename T>
953 constexpr bool operator<(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
954 if (aLHS.isNothing()) {
955 return aRHS.isSome();
957 if (aRHS.isNothing()) {
958 return false;
960 return *aLHS < *aRHS;
963 template <typename T>
964 constexpr bool operator>(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
965 return !(aLHS < aRHS || aLHS == aRHS);
968 template <typename T>
969 constexpr bool operator<=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
970 return aLHS < aRHS || aLHS == aRHS;
973 template <typename T>
974 constexpr bool operator>=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
975 return !(aLHS < aRHS);
978 template <typename T>
979 inline void ImplCycleCollectionTraverse(
980 nsCycleCollectionTraversalCallback& aCallback, mozilla::Maybe<T>& aField,
981 const char* aName, uint32_t aFlags = 0) {
982 if (aField) {
983 ImplCycleCollectionTraverse(aCallback, aField.ref(), aName, aFlags);
987 template <typename T>
988 inline void ImplCycleCollectionUnlink(mozilla::Maybe<T>& aField) {
989 if (aField) {
990 ImplCycleCollectionUnlink(aField.ref());
994 } // namespace mozilla
996 #endif /* mozilla_Maybe_h */