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 "mozilla/Alignment.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/Move.h"
16 #include "mozilla/TypeTraits.h"
18 #include <new> // for placement new
25 * Maybe is a container class which contains either zero or one elements. It
26 * serves two roles. It can represent values which are *semantically* optional,
27 * augmenting a type with an explicit 'Nothing' value. In this role, it provides
28 * methods that make it easy to work with values that may be missing, along with
29 * equality and comparison operators so that Maybe values can be stored in
30 * containers. Maybe values can be constructed conveniently in expressions using
31 * type inference, as follows:
33 * void doSomething(Maybe<Foo> aFoo) {
34 * if (aFoo) // Make sure that aFoo contains a value...
35 * aFoo->takeAction(); // and then use |aFoo->| to access it.
36 * } // |*aFoo| also works!
38 * doSomething(Nothing()); // Passes a Maybe<Foo> containing no value.
39 * doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|.
41 * You'll note that it's important to check whether a Maybe contains a value
42 * before using it, using conversion to bool, |isSome()|, or |isNothing()|. You
43 * can avoid these checks, and sometimes write more readable code, using
44 * |valueOr()|, |ptrOr()|, and |refOr()|, which allow you to retrieve the value
45 * in the Maybe and provide a default for the 'Nothing' case. You can also use
46 * |apply()| to call a function only if the Maybe holds a value, and |map()| to
47 * transform the value in the Maybe, returning another Maybe with a possibly
50 * Maybe's other role is to support lazily constructing objects without using
51 * dynamic storage. A Maybe directly contains storage for a value, but it's
52 * empty by default. |emplace()|, as mentioned above, can be used to construct a
53 * value in Maybe's storage. The value a Maybe contains can be destroyed by
54 * calling |reset()|; this will happen automatically if a Maybe is destroyed
55 * while holding a value.
57 * It's a common idiom in C++ to use a pointer as a 'Maybe' type, with a null
58 * value meaning 'Nothing' and any other value meaning 'Some'. You can convert
59 * from such a pointer to a Maybe value using 'ToMaybe()'.
61 * Maybe is inspired by similar types in the standard library of many other
62 * languages (e.g. Haskell's Maybe and Rust's Option). In the C++ world it's
63 * very similar to std::optional, which was proposed for C++14 and originated in
64 * Boost. The most important differences between Maybe and std::optional are:
66 * - std::optional<T> may be compared with T. We deliberately forbid that.
67 * - std::optional allows in-place construction without a separate call to
68 * |emplace()| by using a dummy |in_place_t| value to tag the appropriate
70 * - std::optional has |valueOr()|, equivalent to Maybe's |valueOr()|, but
71 * lacks corresponding methods for |refOr()| and |ptrOr()|.
72 * - std::optional lacks |map()| and |apply()|, making it less suitable for
73 * functional-style code.
74 * - std::optional lacks many convenience functions that Maybe has. Most
75 * unfortunately, it lacks equivalents of the type-inferred constructor
76 * functions |Some()| and |Nothing()|.
78 * N.B. GCC has missed optimizations with Maybe in the past and may generate
79 * extra branches/loads/stores. Use with caution on hot paths; it's not known
80 * whether or not this is still a problem.
85 typedef void (Maybe::* ConvertibleToBool
)(float*****, double*****);
86 void nonNull(float*****, double*****) {}
89 AlignedStorage2
<T
> mStorage
;
94 Maybe() : mIsSome(false) { }
97 MOZ_IMPLICIT
Maybe(Nothing
) : mIsSome(false) { }
99 Maybe(const Maybe
& aOther
)
102 if (aOther
.mIsSome
) {
107 Maybe(Maybe
&& aOther
)
110 if (aOther
.mIsSome
) {
111 emplace(Move(*aOther
));
116 Maybe
& operator=(const Maybe
& aOther
)
118 if (&aOther
!= this) {
119 if (aOther
.mIsSome
) {
121 // XXX(seth): The correct code for this branch, below, can't be used
122 // due to a bug in Visual Studio 2010. See bug 1052940.
124 ref() = aOther.ref();
138 Maybe
& operator=(Maybe
&& aOther
)
140 MOZ_ASSERT(this != &aOther
, "Self-moves are prohibited");
142 if (aOther
.mIsSome
) {
144 ref() = Move(aOther
.ref());
146 emplace(Move(*aOther
));
156 /* Methods that check whether this Maybe contains a value */
157 operator ConvertibleToBool() const { return mIsSome
? &Maybe::nonNull
: 0; }
158 bool isSome() const { return mIsSome
; }
159 bool isNothing() const { return !mIsSome
; }
161 /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|. */
169 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
170 * the default value provided.
173 T
valueOr(V
&& aDefault
) const
178 return Forward
<V
>(aDefault
);
182 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
183 * the value returned from the function or functor provided.
186 T
valueOrFrom(F
&& aFunc
) const
194 /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|. */
208 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
209 * returns the default value provided.
211 T
* ptrOr(T
* aDefault
)
219 const T
* ptrOr(const T
* aDefault
) const
228 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
229 * returns the value returned from the function or functor provided.
232 T
* ptrOrFrom(F
&& aFunc
)
241 const T
* ptrOrFrom(F
&& aFunc
) const
255 const T
* operator->() const
261 /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
265 return *mStorage
.addr();
271 return *mStorage
.addr();
275 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
276 * the default value provided.
278 T
& refOr(T
& aDefault
)
286 const T
& refOr(const T
& aDefault
) const
295 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
296 * value returned from the function or functor provided.
299 T
& refOrFrom(F
&& aFunc
)
308 const T
& refOrFrom(F
&& aFunc
) const
322 const T
& operator*() const
328 /* If |isSome()|, runs the provided function or functor on the contents of
331 void apply(F
&& aFunc
)
339 void apply(F
&& aFunc
) const
346 /* Variant of |apply()| that takes an additional argument for the function. */
347 template<typename F
, typename A
>
348 void apply(F
&& aFunc
, A
&& aArg
)
351 aFunc(ref(), Forward
<A
>(aArg
));
355 template<typename F
, typename A
>
356 void apply(F
&& aFunc
, A
&& aArg
) const
359 aFunc(ref(), Forward
<A
>(aArg
));
364 * If |isSome()|, runs the provided function and returns the result wrapped
365 * in a Maybe. If |isNothing()|, returns an empty Maybe value.
368 Maybe
<R
> map(R(*aFunc
)(T
&))
372 val
.emplace(aFunc(ref()));
379 Maybe
<R
> map(R(*aFunc
)(const T
&)) const
383 val
.emplace(aFunc(ref()));
389 /* Variant of |map()| that takes an additional argument for the function. */
390 template<typename R
, typename FA
, typename A
>
391 Maybe
<R
> map(R(*aFunc
)(T
&, FA
), A
&& aArg
)
395 val
.emplace(aFunc(ref(), Forward
<A
>(aArg
)));
401 template<typename R
, typename FA
, typename A
>
402 Maybe
<R
> map(R(*aFunc
)(const T
&, FA
), A
&& aArg
) const
406 val
.emplace(aFunc(ref(), Forward
<A
>(aArg
)));
412 /* If |isSome()|, empties this Maybe and destroys its contents. */
422 * Constructs a T value in-place in this empty Maybe<T>'s storage. The
423 * arguments to |emplace()| are the parameters to T's constructor.
425 * WARNING: You can't pass a literal nullptr to these methods without
426 * hitting GCC 4.4-only (and hence B2G-only) compile errors.
430 MOZ_ASSERT(!mIsSome
);
431 ::new (mStorage
.addr()) T();
435 template<typename T1
>
436 void emplace(T1
&& t1
)
438 MOZ_ASSERT(!mIsSome
);
439 ::new (mStorage
.addr()) T(Forward
<T1
>(t1
));
443 template<typename T1
, typename T2
>
444 void emplace(T1
&& t1
, T2
&& t2
)
446 MOZ_ASSERT(!mIsSome
);
447 ::new (mStorage
.addr()) T(Forward
<T1
>(t1
), Forward
<T2
>(t2
));
451 template<typename T1
, typename T2
, typename T3
>
452 void emplace(T1
&& t1
, T2
&& t2
, T3
&& t3
)
454 MOZ_ASSERT(!mIsSome
);
455 ::new (mStorage
.addr()) T(Forward
<T1
>(t1
), Forward
<T2
>(t2
), Forward
<T3
>(t3
));
459 template<typename T1
, typename T2
, typename T3
, typename T4
>
460 void emplace(T1
&& t1
, T2
&& t2
, T3
&& t3
, T4
&& t4
)
462 MOZ_ASSERT(!mIsSome
);
463 ::new (mStorage
.addr()) T(Forward
<T1
>(t1
), Forward
<T2
>(t2
), Forward
<T3
>(t3
),
468 template<typename T1
, typename T2
, typename T3
, typename T4
, typename T5
>
469 void emplace(T1
&& t1
, T2
&& t2
, T3
&& t3
, T4
&& t4
, T5
&& t5
)
471 MOZ_ASSERT(!mIsSome
);
472 ::new (mStorage
.addr()) T(Forward
<T1
>(t1
), Forward
<T2
>(t2
), Forward
<T3
>(t3
),
473 Forward
<T4
>(t4
), Forward
<T5
>(t5
));
477 template<typename T1
, typename T2
, typename T3
, typename T4
, typename T5
,
479 void emplace(T1
&& t1
, T2
&& t2
, T3
&& t3
, T4
&& t4
, T5
&& t5
, T6
&& t6
)
481 MOZ_ASSERT(!mIsSome
);
482 ::new (mStorage
.addr()) T(Forward
<T1
>(t1
), Forward
<T2
>(t2
), Forward
<T3
>(t3
),
483 Forward
<T4
>(t4
), Forward
<T5
>(t5
), Forward
<T6
>(t6
));
487 template<typename T1
, typename T2
, typename T3
, typename T4
, typename T5
,
488 typename T6
, typename T7
>
489 void emplace(T1
&& t1
, T2
&& t2
, T3
&& t3
, T4
&& t4
, T5
&& t5
, T6
&& t6
,
492 MOZ_ASSERT(!mIsSome
);
493 ::new (mStorage
.addr()) T(Forward
<T1
>(t1
), Forward
<T2
>(t2
), Forward
<T3
>(t3
),
494 Forward
<T4
>(t4
), Forward
<T5
>(t5
), Forward
<T6
>(t6
),
499 template<typename T1
, typename T2
, typename T3
, typename T4
, typename T5
,
500 typename T6
, typename T7
, typename T8
>
501 void emplace(T1
&& t1
, T2
&& t2
, T3
&& t3
, T4
&& t4
, T5
&& t5
, T6
&& t6
,
504 MOZ_ASSERT(!mIsSome
);
505 ::new (mStorage
.addr()) T(Forward
<T1
>(t1
), Forward
<T2
>(t2
), Forward
<T3
>(t3
),
506 Forward
<T4
>(t4
), Forward
<T5
>(t5
), Forward
<T6
>(t6
),
507 Forward
<T7
>(t7
), Forward
<T8
>(t8
));
511 template<typename T1
, typename T2
, typename T3
, typename T4
, typename T5
,
512 typename T6
, typename T7
, typename T8
, typename T9
>
513 void emplace(T1
&& t1
, T2
&& t2
, T3
&& t3
, T4
&& t4
, T5
&& t5
, T6
&& t6
,
514 T7
&& t7
, T8
&& t8
, T9
&& t9
)
516 MOZ_ASSERT(!mIsSome
);
517 ::new (mStorage
.addr()) T(Forward
<T1
>(t1
), Forward
<T2
>(t2
), Forward
<T3
>(t3
),
518 Forward
<T4
>(t4
), Forward
<T5
>(t5
), Forward
<T6
>(t6
),
519 Forward
<T7
>(t7
), Forward
<T8
>(t8
), Forward
<T9
>(t9
));
523 template<typename T1
, typename T2
, typename T3
, typename T4
, typename T5
,
524 typename T6
, typename T7
, typename T8
, typename T9
, typename T10
>
525 void emplace(T1
&& t1
, T2
&& t2
, T3
&& t3
, T4
&& t4
, T5
&& t5
, T6
&& t6
,
526 T7
&& t7
, T8
&& t8
, T9
&& t9
, T10
&& t10
)
528 MOZ_ASSERT(!mIsSome
);
529 ::new (mStorage
.addr()) T(Forward
<T1
>(t1
), Forward
<T2
>(t2
), Forward
<T3
>(t3
),
530 Forward
<T4
>(t4
), Forward
<T5
>(t5
), Forward
<T6
>(t6
),
531 Forward
<T7
>(t7
), Forward
<T8
>(t8
), Forward
<T9
>(t9
),
538 * Some() creates a Maybe<T> value containing the provided T value. If T has a
539 * move constructor, it's used to make this as efficient as possible.
541 * Some() selects the type of Maybe it returns by removing any const, volatile,
542 * or reference qualifiers from the type of the value you pass to it. This gives
543 * it more intuitive behavior when used in expressions, but it also means that
544 * if you need to construct a Maybe value that holds a const, volatile, or
545 * reference value, you need to use emplace() instead.
548 Maybe
<typename RemoveCV
<typename RemoveReference
<T
>::Type
>::Type
>
551 typedef typename RemoveCV
<typename RemoveReference
<T
>::Type
>::Type U
;
553 value
.emplace(Forward
<T
>(aValue
));
558 Maybe
<typename RemoveCV
<typename RemoveReference
<T
>::Type
>::Type
>
568 * Two Maybe<T> values are equal if
569 * - both are Nothing, or
570 * - both are Some, and the values they contain are equal.
572 template<typename T
> bool
573 operator==(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
575 if (aLHS
.isNothing() != aRHS
.isNothing()) {
578 return aLHS
.isNothing() || *aLHS
== *aRHS
;
581 template<typename T
> bool
582 operator!=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
584 return !(aLHS
== aRHS
);
588 * We support comparison to Nothing to allow reasonable expressions like:
589 * if (maybeValue == Nothing()) { ... }
591 template<typename T
> bool
592 operator==(const Maybe
<T
>& aLHS
, const Nothing
& aRHS
)
594 return aLHS
.isNothing();
597 template<typename T
> bool
598 operator!=(const Maybe
<T
>& aLHS
, const Nothing
& aRHS
)
600 return !(aLHS
== aRHS
);
603 template<typename T
> bool
604 operator==(const Nothing
& aLHS
, const Maybe
<T
>& aRHS
)
606 return aRHS
.isNothing();
609 template<typename T
> bool
610 operator!=(const Nothing
& aLHS
, const Maybe
<T
>& aRHS
)
612 return !(aLHS
== aRHS
);
616 * Maybe<T> values are ordered in the same way T values are ordered, except that
617 * Nothing comes before anything else.
619 template<typename T
> bool
620 operator<(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
622 if (aLHS
.isNothing()) {
623 return aRHS
.isSome();
625 if (aRHS
.isNothing()) {
628 return *aLHS
< *aRHS
;
631 template<typename T
> bool
632 operator>(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
634 return !(aLHS
< aRHS
|| aLHS
== aRHS
);
637 template<typename T
> bool
638 operator<=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
640 return aLHS
< aRHS
|| aLHS
== aRHS
;
643 template<typename T
> bool
644 operator>=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
646 return !(aLHS
< aRHS
);
649 } // namespace mozilla
651 #endif /* mozilla_Maybe_h */