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.
86 AlignedStorage2
<T
> mStorage
;
91 Maybe() : mIsSome(false) { }
94 MOZ_IMPLICIT
Maybe(Nothing
) : mIsSome(false) { }
96 Maybe(const Maybe
& aOther
)
104 Maybe(Maybe
&& aOther
)
107 if (aOther
.mIsSome
) {
108 emplace(Move(*aOther
));
113 Maybe
& operator=(const Maybe
& aOther
)
115 if (&aOther
!= this) {
116 if (aOther
.mIsSome
) {
118 // XXX(seth): The correct code for this branch, below, can't be used
119 // due to a bug in Visual Studio 2010. See bug 1052940.
121 ref() = aOther.ref();
135 Maybe
& operator=(Maybe
&& aOther
)
137 MOZ_ASSERT(this != &aOther
, "Self-moves are prohibited");
139 if (aOther
.mIsSome
) {
141 ref() = Move(aOther
.ref());
143 emplace(Move(*aOther
));
153 /* Methods that check whether this Maybe contains a value */
154 explicit operator bool() const { return isSome(); }
155 bool isSome() const { return mIsSome
; }
156 bool isNothing() const { return !mIsSome
; }
158 /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|. */
166 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
167 * the default value provided.
170 T
valueOr(V
&& aDefault
) const
175 return Forward
<V
>(aDefault
);
179 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
180 * the value returned from the function or functor provided.
183 T
valueOrFrom(F
&& aFunc
) const
191 /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|. */
205 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
206 * returns the default value provided.
208 T
* ptrOr(T
* aDefault
)
216 const T
* ptrOr(const T
* aDefault
) const
225 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
226 * returns the value returned from the function or functor provided.
229 T
* ptrOrFrom(F
&& aFunc
)
238 const T
* ptrOrFrom(F
&& aFunc
) const
252 const T
* operator->() const
258 /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
262 return *mStorage
.addr();
268 return *mStorage
.addr();
272 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
273 * the default value provided.
275 T
& refOr(T
& aDefault
)
283 const T
& refOr(const T
& aDefault
) const
292 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
293 * value returned from the function or functor provided.
296 T
& refOrFrom(F
&& aFunc
)
305 const T
& refOrFrom(F
&& aFunc
) const
319 const T
& operator*() const
325 /* If |isSome()|, runs the provided function or functor on the contents of
327 template<typename F
, typename
... Args
>
328 void apply(F
&& aFunc
, Args
&&... aArgs
)
331 aFunc(ref(), Forward
<Args
>(aArgs
)...);
335 template<typename F
, typename
... Args
>
336 void apply(F
&& aFunc
, Args
&&... aArgs
) const
339 aFunc(ref(), Forward
<Args
>(aArgs
)...);
344 * If |isSome()|, runs the provided function and returns the result wrapped
345 * in a Maybe. If |isNothing()|, returns an empty Maybe value.
347 template<typename R
, typename
... FArgs
, typename
... Args
>
348 Maybe
<R
> map(R (*aFunc
)(T
&, FArgs
...), Args
&&... aArgs
)
352 val
.emplace(aFunc(ref(), Forward
<Args
>(aArgs
)...));
358 template<typename R
, typename
... FArgs
, typename
... Args
>
359 Maybe
<R
> map(R (*aFunc
)(const T
&, FArgs
...), Args
&&... aArgs
) const
363 val
.emplace(aFunc(ref(), Forward
<Args
>(aArgs
)...));
369 /* If |isSome()|, empties this Maybe and destroys its contents. */
379 * Constructs a T value in-place in this empty Maybe<T>'s storage. The
380 * arguments to |emplace()| are the parameters to T's constructor.
382 template<typename
... Args
>
383 void emplace(Args
&&... aArgs
)
385 MOZ_ASSERT(!mIsSome
);
386 ::new (mStorage
.addr()) T(Forward
<Args
>(aArgs
)...);
392 * Some() creates a Maybe<T> value containing the provided T value. If T has a
393 * move constructor, it's used to make this as efficient as possible.
395 * Some() selects the type of Maybe it returns by removing any const, volatile,
396 * or reference qualifiers from the type of the value you pass to it. This gives
397 * it more intuitive behavior when used in expressions, but it also means that
398 * if you need to construct a Maybe value that holds a const, volatile, or
399 * reference value, you need to use emplace() instead.
402 Maybe
<typename RemoveCV
<typename RemoveReference
<T
>::Type
>::Type
>
405 typedef typename RemoveCV
<typename RemoveReference
<T
>::Type
>::Type U
;
407 value
.emplace(Forward
<T
>(aValue
));
412 Maybe
<typename RemoveCV
<typename RemoveReference
<T
>::Type
>::Type
>
422 * Two Maybe<T> values are equal if
423 * - both are Nothing, or
424 * - both are Some, and the values they contain are equal.
426 template<typename T
> bool
427 operator==(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
429 if (aLHS
.isNothing() != aRHS
.isNothing()) {
432 return aLHS
.isNothing() || *aLHS
== *aRHS
;
435 template<typename T
> bool
436 operator!=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
438 return !(aLHS
== aRHS
);
442 * We support comparison to Nothing to allow reasonable expressions like:
443 * if (maybeValue == Nothing()) { ... }
445 template<typename T
> bool
446 operator==(const Maybe
<T
>& aLHS
, const Nothing
& aRHS
)
448 return aLHS
.isNothing();
451 template<typename T
> bool
452 operator!=(const Maybe
<T
>& aLHS
, const Nothing
& aRHS
)
454 return !(aLHS
== aRHS
);
457 template<typename T
> bool
458 operator==(const Nothing
& aLHS
, const Maybe
<T
>& aRHS
)
460 return aRHS
.isNothing();
463 template<typename T
> bool
464 operator!=(const Nothing
& aLHS
, const Maybe
<T
>& aRHS
)
466 return !(aLHS
== aRHS
);
470 * Maybe<T> values are ordered in the same way T values are ordered, except that
471 * Nothing comes before anything else.
473 template<typename T
> bool
474 operator<(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
476 if (aLHS
.isNothing()) {
477 return aRHS
.isSome();
479 if (aRHS
.isNothing()) {
482 return *aLHS
< *aRHS
;
485 template<typename T
> bool
486 operator>(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
488 return !(aLHS
< aRHS
|| aLHS
== aRHS
);
491 template<typename T
> bool
492 operator<=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
494 return aLHS
< aRHS
|| aLHS
== aRHS
;
497 template<typename T
> bool
498 operator>=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
)
500 return !(aLHS
< aRHS
);
503 } // namespace mozilla
505 #endif /* mozilla_Maybe_h */