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/OperatorNewExtensions.h"
17 #include "mozilla/TypeTraits.h"
19 #include <new> // for placement new
21 #include <type_traits>
28 * Maybe is a container class which contains either zero or one elements. It
29 * serves two roles. It can represent values which are *semantically* optional,
30 * augmenting a type with an explicit 'Nothing' value. In this role, it provides
31 * methods that make it easy to work with values that may be missing, along with
32 * equality and comparison operators so that Maybe values can be stored in
33 * containers. Maybe values can be constructed conveniently in expressions using
34 * type inference, as follows:
36 * void doSomething(Maybe<Foo> aFoo) {
37 * if (aFoo) // Make sure that aFoo contains a value...
38 * aFoo->takeAction(); // and then use |aFoo->| to access it.
39 * } // |*aFoo| also works!
41 * doSomething(Nothing()); // Passes a Maybe<Foo> containing no value.
42 * doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|.
44 * You'll note that it's important to check whether a Maybe contains a value
45 * before using it, using conversion to bool, |isSome()|, or |isNothing()|. You
46 * can avoid these checks, and sometimes write more readable code, using
47 * |valueOr()|, |ptrOr()|, and |refOr()|, which allow you to retrieve the value
48 * in the Maybe and provide a default for the 'Nothing' case. You can also use
49 * |apply()| to call a function only if the Maybe holds a value, and |map()| to
50 * transform the value in the Maybe, returning another Maybe with a possibly
53 * Maybe's other role is to support lazily constructing objects without using
54 * dynamic storage. A Maybe directly contains storage for a value, but it's
55 * empty by default. |emplace()|, as mentioned above, can be used to construct a
56 * value in Maybe's storage. The value a Maybe contains can be destroyed by
57 * calling |reset()|; this will happen automatically if a Maybe is destroyed
58 * while holding a value.
60 * It's a common idiom in C++ to use a pointer as a 'Maybe' type, with a null
61 * value meaning 'Nothing' and any other value meaning 'Some'. You can convert
62 * from such a pointer to a Maybe value using 'ToMaybe()'.
64 * Maybe is inspired by similar types in the standard library of many other
65 * languages (e.g. Haskell's Maybe and Rust's Option). In the C++ world it's
66 * very similar to std::optional, which was proposed for C++14 and originated in
67 * Boost. The most important differences between Maybe and std::optional are:
69 * - std::optional<T> may be compared with T. We deliberately forbid that.
70 * - std::optional allows in-place construction without a separate call to
71 * |emplace()| by using a dummy |in_place_t| value to tag the appropriate
73 * - std::optional has |valueOr()|, equivalent to Maybe's |valueOr()|, but
74 * lacks corresponding methods for |refOr()| and |ptrOr()|.
75 * - std::optional lacks |map()| and |apply()|, making it less suitable for
76 * functional-style code.
77 * - std::optional lacks many convenience functions that Maybe has. Most
78 * unfortunately, it lacks equivalents of the type-inferred constructor
79 * functions |Some()| and |Nothing()|.
81 * N.B. GCC has missed optimizations with Maybe in the past and may generate
82 * extra branches/loads/stores. Use with caution on hot paths; it's not known
83 * whether or not this is still a problem.
86 class MOZ_NON_PARAM MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe
88 MOZ_ALIGNAS_IN_STRUCT(T
) unsigned char mStorage
[sizeof(T
)];
89 char mIsSome
; // not bool -- guarantees minimal space consumption
91 // GCC fails due to -Werror=strict-aliasing if |mStorage| is directly cast to
92 // T*. Indirecting through these functions addresses the problem.
93 void* data() { return mStorage
; }
94 const void* data() const { return mStorage
; }
99 Maybe() : mIsSome(false) { }
100 ~Maybe() { reset(); }
102 MOZ_IMPLICIT
Maybe(Nothing
) : mIsSome(false) { }
104 Maybe(const Maybe
& aOther
)
107 if (aOther
.mIsSome
) {
113 * Maybe<T> can be copy-constructed from a Maybe<U> if U is convertible to T.
117 typename
std::enable_if
<std::is_convertible
<U
, T
>::value
>::type
>
119 Maybe(const Maybe
<U
>& aOther
)
122 if (aOther
.isSome()) {
127 Maybe(Maybe
&& aOther
)
130 if (aOther
.mIsSome
) {
131 emplace(Move(*aOther
));
137 * Maybe<T> can be move-constructed from a Maybe<U> if U is convertible to T.
141 typename
std::enable_if
<std::is_convertible
<U
, T
>::value
>::type
>
143 Maybe(Maybe
<U
>&& aOther
)
146 if (aOther
.isSome()) {
147 emplace(Move(*aOther
));
152 Maybe
& operator=(const Maybe
& aOther
)
154 if (&aOther
!= this) {
155 if (aOther
.mIsSome
) {
157 ref() = aOther
.ref();
170 typename
std::enable_if
<std::is_convertible
<U
, T
>::value
>::type
>
171 Maybe
& operator=(const Maybe
<U
>& aOther
)
173 if (aOther
.isSome()) {
175 ref() = aOther
.ref();
185 Maybe
& operator=(Maybe
&& aOther
)
187 MOZ_ASSERT(this != &aOther
, "Self-moves are prohibited");
189 if (aOther
.mIsSome
) {
191 ref() = Move(aOther
.ref());
193 emplace(Move(*aOther
));
205 typename
std::enable_if
<std::is_convertible
<U
, T
>::value
>::type
>
206 Maybe
& operator=(Maybe
<U
>&& aOther
)
208 if (aOther
.isSome()) {
210 ref() = Move(aOther
.ref());
212 emplace(Move(*aOther
));
222 /* Methods that check whether this Maybe contains a value */
223 explicit operator bool() const { return isSome(); }
224 bool isSome() const { return mIsSome
; }
225 bool isNothing() const { return !mIsSome
; }
227 /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|. */
235 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
236 * the default value provided.
239 T
valueOr(V
&& aDefault
) const
244 return Forward
<V
>(aDefault
);
248 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
249 * the value returned from the function or functor provided.
252 T
valueOrFrom(F
&& aFunc
) const
260 /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|. */
274 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
275 * returns the default value provided.
277 T
* ptrOr(T
* aDefault
)
285 const T
* ptrOr(const T
* aDefault
) const
294 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
295 * returns the value returned from the function or functor provided.
298 T
* ptrOrFrom(F
&& aFunc
)
307 const T
* ptrOrFrom(F
&& aFunc
) const
321 const T
* operator->() const
327 /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
331 return *static_cast<T
*>(data());
337 return *static_cast<const T
*>(data());
341 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
342 * the default value provided.
344 T
& refOr(T
& aDefault
)
352 const T
& refOr(const T
& aDefault
) const
361 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
362 * value returned from the function or functor provided.
365 T
& refOrFrom(F
&& aFunc
)
374 const T
& refOrFrom(F
&& aFunc
) const
388 const T
& operator*() const
394 /* If |isSome()|, runs the provided function or functor on the contents of
396 template<typename Func
>
397 Maybe
& apply(Func aFunc
)
405 template<typename Func
>
406 const Maybe
& apply(Func aFunc
) const
415 * If |isSome()|, runs the provided function and returns the result wrapped
416 * in a Maybe. If |isNothing()|, returns an empty Maybe value.
418 template<typename Func
>
419 auto map(Func aFunc
) -> Maybe
<decltype(aFunc(DeclVal
<Maybe
<T
>>().ref()))>
421 using ReturnType
= decltype(aFunc(ref()));
423 Maybe
<ReturnType
> val
;
424 val
.emplace(aFunc(ref()));
427 return Maybe
<ReturnType
>();
430 template<typename Func
>
431 auto map(Func aFunc
) const -> Maybe
<decltype(aFunc(DeclVal
<Maybe
<T
>>().ref()))>
433 using ReturnType
= decltype(aFunc(ref()));
435 Maybe
<ReturnType
> val
;
436 val
.emplace(aFunc(ref()));
439 return Maybe
<ReturnType
>();
442 /* If |isSome()|, empties this Maybe and destroys its contents. */
452 * Constructs a T value in-place in this empty Maybe<T>'s storage. The
453 * arguments to |emplace()| are the parameters to T's constructor.
455 template<typename
... Args
>
456 void emplace(Args
&&... aArgs
)
458 MOZ_ASSERT(!mIsSome
);
459 ::new (KnownNotNull
, data()) T(Forward
<Args
>(aArgs
)...);
464 operator<<(std::ostream
& aStream
, const Maybe
<T
>& aMaybe
)
467 aStream
<< aMaybe
.ref();
469 aStream
<< "<Nothing>";
476 * Some() creates a Maybe<T> value containing the provided T value. If T has a
477 * move constructor, it's used to make this as efficient as possible.
479 * Some() selects the type of Maybe it returns by removing any const, volatile,
480 * or reference qualifiers from the type of the value you pass to it. This gives
481 * it more intuitive behavior when used in expressions, but it also means that
482 * if you need to construct a Maybe value that holds a const, volatile, or
483 * reference value, you need to use emplace() instead.
486 typename U
= typename
std::remove_cv
<
487 typename
std::remove_reference
<T
>::type
>::type
>
492 value
.emplace(Forward
<T
>(aValue
));
497 Maybe
<typename RemoveCV
<typename RemoveReference
<T
>::Type
>::Type
>
507 * Two Maybe<T> values are equal if
508 * - both are Nothing, or
509 * - both are Some, and the values they contain are equal.
511 template<typename T
> bool
512 operator==(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
514 if (aLHS
.isNothing() != aRHS
.isNothing()) {
517 return aLHS
.isNothing() || *aLHS
== *aRHS
;
520 template<typename T
> bool
521 operator!=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
523 return !(aLHS
== aRHS
);
527 * We support comparison to Nothing to allow reasonable expressions like:
528 * if (maybeValue == Nothing()) { ... }
530 template<typename T
> bool
531 operator==(const Maybe
<T
>& aLHS
, const Nothing
& aRHS
)
533 return aLHS
.isNothing();
536 template<typename T
> bool
537 operator!=(const Maybe
<T
>& aLHS
, const Nothing
& aRHS
)
539 return !(aLHS
== aRHS
);
542 template<typename T
> bool
543 operator==(const Nothing
& aLHS
, const Maybe
<T
>& aRHS
)
545 return aRHS
.isNothing();
548 template<typename T
> bool
549 operator!=(const Nothing
& aLHS
, const Maybe
<T
>& aRHS
)
551 return !(aLHS
== aRHS
);
555 * Maybe<T> values are ordered in the same way T values are ordered, except that
556 * Nothing comes before anything else.
558 template<typename T
> bool
559 operator<(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
561 if (aLHS
.isNothing()) {
562 return aRHS
.isSome();
564 if (aRHS
.isNothing()) {
567 return *aLHS
< *aRHS
;
570 template<typename T
> bool
571 operator>(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
573 return !(aLHS
< aRHS
|| aLHS
== aRHS
);
576 template<typename T
> bool
577 operator<=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
579 return aLHS
< aRHS
|| aLHS
== aRHS
;
582 template<typename T
> bool
583 operator>=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
585 return !(aLHS
< aRHS
);
588 } // namespace mozilla
590 #endif /* mozilla_Maybe_h */