1 /* -*- Mode: C++; tab-width: 8; 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
14 #include <type_traits>
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
;
27 inline void CycleCollectionNoteChild(
28 nsCycleCollectionTraversalCallback
& aCallback
, T
* aChild
, const char* aName
,
35 inline constexpr bool operator==(const Nothing
&, const Nothing
&) {
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
,
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
);
68 struct InlinePoisoner
<N
, N
> {
69 static void poison(void*, const uintptr_t) {
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
);
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
);
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
));
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))); \
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> {
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();
173 template <typename T
>
174 class Maybe_CopyMove_Enabler
<T
, true, false, true> {
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();
192 template <typename T
>
193 class Maybe_CopyMove_Enabler
<T
, false, true, true> {
195 Maybe_CopyMove_Enabler() = default;
204 template <typename T
>
205 class Maybe_CopyMove_Enabler
<T
, false, false, true> {
207 Maybe_CopyMove_Enabler() = default;
215 template <typename T
>
216 class Maybe_CopyMove_Enabler
<T
, false, true, false> {
218 Maybe_CopyMove_Enabler() = default;
226 template <typename T
, bool TriviallyDestructibleAndCopyable
>
227 class Maybe_CopyMove_Enabler
<T
, TriviallyDestructibleAndCopyable
, false,
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
>>
246 template <typename T
>
247 struct MaybeStorage
<T
, false> {
248 using NonConstT
= std::remove_const_t
<T
>;
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
)} {}
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 // Copy and move operations are no-ops, since copying is moving is implemented
269 // by Maybe_CopyMove_Enabler.
271 MaybeStorage(const MaybeStorage
&) {}
272 MaybeStorage
& operator=(const MaybeStorage
&) { return *this; }
273 MaybeStorage(MaybeStorage
&&) {}
274 MaybeStorage
& operator=(MaybeStorage
&&) { return *this; }
278 mStorage
.val
.T::~T();
283 template <typename T
>
284 struct MaybeStorage
<T
, true> {
285 using NonConstT
= std::remove_const_t
<T
>;
288 constexpr Union() : dummy() {}
289 constexpr explicit Union(const T
& aVal
) : val
{aVal
} {}
290 constexpr explicit Union(T
&& aVal
) : val
{std::move(aVal
)} {}
295 char mIsSome
= false; // not bool -- guarantees minimal space consumption
297 constexpr MaybeStorage() = default;
298 constexpr explicit MaybeStorage(const T
& aVal
)
299 : mStorage
{aVal
}, mIsSome
{true} {}
300 constexpr explicit MaybeStorage(T
&& aVal
)
301 : mStorage
{std::move(aVal
)}, mIsSome
{true} {}
304 } // namespace detail
306 template <typename T
, typename U
= typename
std::remove_cv
<
307 typename
std::remove_reference
<T
>::type
>::type
>
308 constexpr Maybe
<U
> Some(T
&& aValue
);
311 * Maybe is a container class which contains either zero or one elements. It
312 * serves two roles. It can represent values which are *semantically* optional,
313 * augmenting a type with an explicit 'Nothing' value. In this role, it provides
314 * methods that make it easy to work with values that may be missing, along with
315 * equality and comparison operators so that Maybe values can be stored in
316 * containers. Maybe values can be constructed conveniently in expressions using
317 * type inference, as follows:
319 * void doSomething(Maybe<Foo> aFoo) {
320 * if (aFoo) // Make sure that aFoo contains a value...
321 * aFoo->takeAction(); // and then use |aFoo->| to access it.
322 * } // |*aFoo| also works!
324 * doSomething(Nothing()); // Passes a Maybe<Foo> containing no value.
325 * doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|.
327 * You'll note that it's important to check whether a Maybe contains a value
328 * before using it, using conversion to bool, |isSome()|, or |isNothing()|. You
329 * can avoid these checks, and sometimes write more readable code, using
330 * |valueOr()|, |ptrOr()|, and |refOr()|, which allow you to retrieve the value
331 * in the Maybe and provide a default for the 'Nothing' case. You can also use
332 * |apply()| to call a function only if the Maybe holds a value, and |map()| to
333 * transform the value in the Maybe, returning another Maybe with a possibly
336 * Maybe's other role is to support lazily constructing objects without using
337 * dynamic storage. A Maybe directly contains storage for a value, but it's
338 * empty by default. |emplace()|, as mentioned above, can be used to construct a
339 * value in Maybe's storage. The value a Maybe contains can be destroyed by
340 * calling |reset()|; this will happen automatically if a Maybe is destroyed
341 * while holding a value.
343 * It's a common idiom in C++ to use a pointer as a 'Maybe' type, with a null
344 * value meaning 'Nothing' and any other value meaning 'Some'. You can convert
345 * from such a pointer to a Maybe value using 'ToMaybe()'.
347 * Maybe is inspired by similar types in the standard library of many other
348 * languages (e.g. Haskell's Maybe and Rust's Option). In the C++ world it's
349 * very similar to std::optional, which was proposed for C++14 and originated in
350 * Boost. The most important differences between Maybe and std::optional are:
352 * - std::optional<T> may be compared with T. We deliberately forbid that.
353 * - std::optional allows in-place construction without a separate call to
354 * |emplace()| by using a dummy |in_place_t| value to tag the appropriate
356 * - std::optional has |valueOr()|, equivalent to Maybe's |valueOr()|, but
357 * lacks corresponding methods for |refOr()| and |ptrOr()|.
358 * - std::optional lacks |map()| and |apply()|, making it less suitable for
359 * functional-style code.
360 * - std::optional lacks many convenience functions that Maybe has. Most
361 * unfortunately, it lacks equivalents of the type-inferred constructor
362 * functions |Some()| and |Nothing()|.
365 class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe
366 : private detail::MaybeStorage
<T
>,
367 public detail::Maybe_CopyMove_Enabler
<T
> {
368 template <typename
, bool, bool, bool>
369 friend class detail::Maybe_CopyMove_Enabler
;
371 template <typename U
, typename V
>
372 friend constexpr Maybe
<V
> Some(U
&& aValue
);
376 template <typename U
>
377 constexpr Maybe(U
&& aValue
, SomeGuard
)
378 : detail::MaybeStorage
<T
>{std::forward
<U
>(aValue
)} {}
380 using detail::MaybeStorage
<T
>::mIsSome
;
381 using detail::MaybeStorage
<T
>::mStorage
;
383 void poisonData() { detail::MaybePoisoner
<T
>::poison(&mStorage
.val
); }
388 MOZ_ALLOW_TEMPORARY
constexpr Maybe() = default;
390 MOZ_ALLOW_TEMPORARY MOZ_IMPLICIT
constexpr Maybe(Nothing
) : Maybe
{} {}
393 * Maybe<T> can be copy-constructed from a Maybe<U> if T is constructible from
396 template <typename U
,
397 typename
= std::enable_if_t
<std::is_constructible_v
<T
, const U
&>>>
398 MOZ_IMPLICIT
Maybe(const Maybe
<U
>& aOther
) {
399 if (aOther
.isSome()) {
405 * Maybe<T> can be move-constructed from a Maybe<U> if T is constructible from
408 template <typename U
,
409 typename
= std::enable_if_t
<std::is_constructible_v
<T
, U
&&>>>
410 MOZ_IMPLICIT
Maybe(Maybe
<U
>&& aOther
) {
411 if (aOther
.isSome()) {
412 emplace(std::move(*aOther
));
417 template <typename U
,
418 typename
= std::enable_if_t
<std::is_constructible_v
<T
, const U
&>>>
419 Maybe
& operator=(const Maybe
<U
>& aOther
) {
420 if (aOther
.isSome()) {
422 ref() = aOther
.ref();
432 template <typename U
,
433 typename
= std::enable_if_t
<std::is_constructible_v
<T
, U
&&>>>
434 Maybe
& operator=(Maybe
<U
>&& aOther
) {
435 if (aOther
.isSome()) {
437 ref() = std::move(aOther
.ref());
439 emplace(std::move(*aOther
));
449 constexpr Maybe
& operator=(Nothing
) {
454 /* Methods that check whether this Maybe contains a value */
455 constexpr explicit operator bool() const { return isSome(); }
456 constexpr bool isSome() const { return mIsSome
; }
457 constexpr bool isNothing() const { return !mIsSome
; }
459 /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|.
461 constexpr T
value() const;
464 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
465 * the default value provided.
467 template <typename V
>
468 constexpr T
valueOr(V
&& aDefault
) const {
472 return std::forward
<V
>(aDefault
);
476 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
477 * the value returned from the function or functor provided.
479 template <typename F
>
480 constexpr T
valueOrFrom(F
&& aFunc
) const {
487 /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|.
490 constexpr const T
* ptr() const;
493 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
494 * returns the default value provided.
496 T
* ptrOr(T
* aDefault
) {
503 constexpr const T
* ptrOr(const T
* aDefault
) const {
511 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
512 * returns the value returned from the function or functor provided.
514 template <typename F
>
515 T
* ptrOrFrom(F
&& aFunc
) {
522 template <typename F
>
523 const T
* ptrOrFrom(F
&& aFunc
) const {
530 constexpr T
* operator->();
531 constexpr const T
* operator->() const;
533 /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
535 constexpr const T
& ref() const;
538 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
539 * the default value provided.
541 constexpr T
& refOr(T
& aDefault
) {
548 constexpr const T
& refOr(const T
& aDefault
) const {
556 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
557 * value returned from the function or functor provided.
559 template <typename F
>
560 constexpr T
& refOrFrom(F
&& aFunc
) {
567 template <typename F
>
568 constexpr const T
& refOrFrom(F
&& aFunc
) const {
575 constexpr T
& operator*();
576 constexpr const T
& operator*() const;
578 /* If |isSome()|, runs the provided function or functor on the contents of
580 template <typename Func
>
581 constexpr Maybe
& apply(Func
&& aFunc
) {
583 std::forward
<Func
>(aFunc
)(ref());
588 template <typename Func
>
589 constexpr const Maybe
& apply(Func
&& aFunc
) const {
591 std::forward
<Func
>(aFunc
)(ref());
597 * If |isSome()|, runs the provided function and returns the result wrapped
598 * in a Maybe. If |isNothing()|, returns an empty Maybe value with the same
599 * value type as what the provided function would have returned.
601 template <typename Func
>
602 constexpr auto map(Func
&& aFunc
) {
604 return Some(std::forward
<Func
>(aFunc
)(ref()));
606 return Maybe
<decltype(std::forward
<Func
>(aFunc
)(ref()))>{};
609 template <typename Func
>
610 constexpr auto map(Func
&& aFunc
) const {
612 return Some(std::forward
<Func
>(aFunc
)(ref()));
614 return Maybe
<decltype(std::forward
<Func
>(aFunc
)(ref()))>{};
617 /* If |isSome()|, empties this Maybe and destroys its contents. */
618 constexpr void reset() {
620 if constexpr (!std::is_trivially_destructible_v
<T
>) {
629 * Constructs a T value in-place in this empty Maybe<T>'s storage. The
630 * arguments to |emplace()| are the parameters to T's constructor.
632 template <typename
... Args
>
633 constexpr void emplace(Args
&&... aArgs
);
635 template <typename U
>
636 constexpr std::enable_if_t
<std::is_same_v
<T
, U
> &&
637 std::is_copy_constructible_v
<U
> &&
638 !std::is_move_constructible_v
<U
>>
643 friend std::ostream
& operator<<(std::ostream
& aStream
,
644 const Maybe
<T
>& aMaybe
) {
646 aStream
<< aMaybe
.ref();
648 aStream
<< "<Nothing>";
654 template <typename T
>
657 constexpr Maybe() = default;
658 constexpr MOZ_IMPLICIT
Maybe(Nothing
) {}
660 void emplace(T
& aRef
) { mValue
= &aRef
; }
662 /* Methods that check whether this Maybe contains a value */
663 constexpr explicit operator bool() const { return isSome(); }
664 constexpr bool isSome() const { return mValue
; }
665 constexpr bool isNothing() const { return !mValue
; }
668 MOZ_DIAGNOSTIC_ASSERT(isSome());
672 T
* operator->() const { return &ref(); }
673 T
& operator*() const { return ref(); }
675 // Deliberately not defining value and ptr accessors, as these may be
676 // confusing on a reference-typed Maybe.
678 // XXX Should we define refOr?
680 void reset() { mValue
= nullptr; }
682 template <typename Func
>
683 Maybe
& apply(Func
&& aFunc
) {
685 std::forward
<Func
>(aFunc
)(ref());
690 template <typename Func
>
691 const Maybe
& apply(Func
&& aFunc
) const {
693 std::forward
<Func
>(aFunc
)(ref());
698 template <typename Func
>
699 auto map(Func
&& aFunc
) {
700 Maybe
<decltype(std::forward
<Func
>(aFunc
)(ref()))> val
;
702 val
.emplace(std::forward
<Func
>(aFunc
)(ref()));
707 template <typename Func
>
708 auto map(Func
&& aFunc
) const {
709 Maybe
<decltype(std::forward
<Func
>(aFunc
)(ref()))> val
;
711 val
.emplace(std::forward
<Func
>(aFunc
)(ref()));
720 template <typename T
>
721 constexpr T Maybe
<T
>::value() const {
722 MOZ_DIAGNOSTIC_ASSERT(isSome());
726 template <typename T
>
728 MOZ_DIAGNOSTIC_ASSERT(isSome());
732 template <typename T
>
733 constexpr const T
* Maybe
<T
>::ptr() const {
734 MOZ_DIAGNOSTIC_ASSERT(isSome());
738 template <typename T
>
739 constexpr T
* Maybe
<T
>::operator->() {
740 MOZ_DIAGNOSTIC_ASSERT(isSome());
744 template <typename T
>
745 constexpr const T
* Maybe
<T
>::operator->() const {
746 MOZ_DIAGNOSTIC_ASSERT(isSome());
750 template <typename T
>
751 constexpr T
& Maybe
<T
>::ref() {
752 MOZ_DIAGNOSTIC_ASSERT(isSome());
756 template <typename T
>
757 constexpr const T
& Maybe
<T
>::ref() const {
758 MOZ_DIAGNOSTIC_ASSERT(isSome());
762 template <typename T
>
763 constexpr T
& Maybe
<T
>::operator*() {
764 MOZ_DIAGNOSTIC_ASSERT(isSome());
768 template <typename T
>
769 constexpr const T
& Maybe
<T
>::operator*() const {
770 MOZ_DIAGNOSTIC_ASSERT(isSome());
774 template <typename T
>
775 template <typename
... Args
>
776 constexpr void Maybe
<T
>::emplace(Args
&&... aArgs
) {
777 MOZ_DIAGNOSTIC_ASSERT(!isSome());
778 ::new (KnownNotNull
, &mStorage
.val
) T(std::forward
<Args
>(aArgs
)...);
783 * Some() creates a Maybe<T> value containing the provided T value. If T has a
784 * move constructor, it's used to make this as efficient as possible.
786 * Some() selects the type of Maybe it returns by removing any const, volatile,
787 * or reference qualifiers from the type of the value you pass to it. This gives
788 * it more intuitive behavior when used in expressions, but it also means that
789 * if you need to construct a Maybe value that holds a const, volatile, or
790 * reference value, you need to use emplace() instead.
792 template <typename T
, typename U
>
793 constexpr Maybe
<U
> Some(T
&& aValue
) {
794 return {std::forward
<T
>(aValue
), typename Maybe
<U
>::SomeGuard
{}};
797 template <typename T
>
798 constexpr Maybe
<T
&> SomeRef(T
& aValue
) {
800 value
.emplace(aValue
);
804 template <typename T
>
805 Maybe
<std::remove_cv_t
<std::remove_reference_t
<T
>>> ToMaybe(T
* aPtr
) {
813 * Two Maybe<T> values are equal if
814 * - both are Nothing, or
815 * - both are Some, and the values they contain are equal.
817 template <typename T
>
818 constexpr bool operator==(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
) {
819 static_assert(!std::is_reference_v
<T
>,
820 "operator== is not defined for Maybe<T&>, compare values or "
821 "addresses explicitly instead");
822 if (aLHS
.isNothing() != aRHS
.isNothing()) {
825 return aLHS
.isNothing() || *aLHS
== *aRHS
;
828 template <typename T
>
829 constexpr bool operator!=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
) {
830 return !(aLHS
== aRHS
);
834 * We support comparison to Nothing to allow reasonable expressions like:
835 * if (maybeValue == Nothing()) { ... }
837 template <typename T
>
838 constexpr bool operator==(const Maybe
<T
>& aLHS
, const Nothing
& aRHS
) {
839 return aLHS
.isNothing();
842 template <typename T
>
843 constexpr bool operator!=(const Maybe
<T
>& aLHS
, const Nothing
& aRHS
) {
844 return !(aLHS
== aRHS
);
847 template <typename T
>
848 constexpr bool operator==(const Nothing
& aLHS
, const Maybe
<T
>& aRHS
) {
849 return aRHS
.isNothing();
852 template <typename T
>
853 constexpr bool operator!=(const Nothing
& aLHS
, const Maybe
<T
>& aRHS
) {
854 return !(aLHS
== aRHS
);
858 * Maybe<T> values are ordered in the same way T values are ordered, except that
859 * Nothing comes before anything else.
861 template <typename T
>
862 constexpr bool operator<(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
) {
863 if (aLHS
.isNothing()) {
864 return aRHS
.isSome();
866 if (aRHS
.isNothing()) {
869 return *aLHS
< *aRHS
;
872 template <typename T
>
873 constexpr bool operator>(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
) {
874 return !(aLHS
< aRHS
|| aLHS
== aRHS
);
877 template <typename T
>
878 constexpr bool operator<=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
) {
879 return aLHS
< aRHS
|| aLHS
== aRHS
;
882 template <typename T
>
883 constexpr bool operator>=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
) {
884 return !(aLHS
< aRHS
);
887 template <typename T
>
888 inline void ImplCycleCollectionTraverse(
889 nsCycleCollectionTraversalCallback
& aCallback
, mozilla::Maybe
<T
>& aField
,
890 const char* aName
, uint32_t aFlags
= 0) {
892 ImplCycleCollectionTraverse(aCallback
, aField
.ref(), aName
, aFlags
);
896 template <typename T
>
897 inline void ImplCycleCollectionUnlink(mozilla::Maybe
<T
>& aField
) {
899 ImplCycleCollectionUnlink(aField
.ref());
903 } // namespace mozilla
905 #endif /* mozilla_Maybe_h */