Fix typo in 9b54bd30006c008b4a951331b273613d5bac3abf
[pm.git] / mfbt / Maybe.h
blob6c2ddf68990882b6758f401e0c6067d660bffcef
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
20 namespace mozilla {
22 struct Nothing { };
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
48 * different type.
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
69 * constructor.
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.
82 template<class T>
83 class Maybe
85 bool mIsSome;
86 AlignedStorage2<T> mStorage;
88 public:
89 typedef T ValueType;
91 Maybe() : mIsSome(false) { }
92 ~Maybe() { reset(); }
94 MOZ_IMPLICIT Maybe(Nothing) : mIsSome(false) { }
96 Maybe(const Maybe& aOther)
97 : mIsSome(false)
99 if (aOther.mIsSome) {
100 emplace(*aOther);
104 Maybe(Maybe&& aOther)
105 : mIsSome(false)
107 if (aOther.mIsSome) {
108 emplace(Move(*aOther));
109 aOther.reset();
113 Maybe& operator=(const Maybe& aOther)
115 if (&aOther != this) {
116 if (aOther.mIsSome) {
117 if (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();
123 reset();
124 emplace(*aOther);
125 } else {
126 emplace(*aOther);
128 } else {
129 reset();
132 return *this;
135 Maybe& operator=(Maybe&& aOther)
137 MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
139 if (aOther.mIsSome) {
140 if (mIsSome) {
141 ref() = Move(aOther.ref());
142 } else {
143 emplace(Move(*aOther));
145 aOther.reset();
146 } else {
147 reset();
150 return *this;
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()|. */
159 T value() const
161 MOZ_ASSERT(mIsSome);
162 return ref();
166 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
167 * the default value provided.
169 template<typename V>
170 T valueOr(V&& aDefault) const
172 if (isSome()) {
173 return ref();
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.
182 template<typename F>
183 T valueOrFrom(F&& aFunc) const
185 if (isSome()) {
186 return ref();
188 return aFunc();
191 /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|. */
192 T* ptr()
194 MOZ_ASSERT(mIsSome);
195 return &ref();
198 const T* ptr() const
200 MOZ_ASSERT(mIsSome);
201 return &ref();
205 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
206 * returns the default value provided.
208 T* ptrOr(T* aDefault)
210 if (isSome()) {
211 return ptr();
213 return aDefault;
216 const T* ptrOr(const T* aDefault) const
218 if (isSome()) {
219 return ptr();
221 return aDefault;
225 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
226 * returns the value returned from the function or functor provided.
228 template<typename F>
229 T* ptrOrFrom(F&& aFunc)
231 if (isSome()) {
232 return ptr();
234 return aFunc();
237 template<typename F>
238 const T* ptrOrFrom(F&& aFunc) const
240 if (isSome()) {
241 return ptr();
243 return aFunc();
246 T* operator->()
248 MOZ_ASSERT(mIsSome);
249 return ptr();
252 const T* operator->() const
254 MOZ_ASSERT(mIsSome);
255 return ptr();
258 /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
259 T& ref()
261 MOZ_ASSERT(mIsSome);
262 return *mStorage.addr();
265 const T& ref() const
267 MOZ_ASSERT(mIsSome);
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)
277 if (isSome()) {
278 return ref();
280 return aDefault;
283 const T& refOr(const T& aDefault) const
285 if (isSome()) {
286 return ref();
288 return aDefault;
292 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
293 * value returned from the function or functor provided.
295 template<typename F>
296 T& refOrFrom(F&& aFunc)
298 if (isSome()) {
299 return ref();
301 return aFunc();
304 template<typename F>
305 const T& refOrFrom(F&& aFunc) const
307 if (isSome()) {
308 return ref();
310 return aFunc();
313 T& operator*()
315 MOZ_ASSERT(mIsSome);
316 return ref();
319 const T& operator*() const
321 MOZ_ASSERT(mIsSome);
322 return ref();
325 /* If |isSome()|, runs the provided function or functor on the contents of
326 * this Maybe. */
327 template<typename F, typename... Args>
328 void apply(F&& aFunc, Args&&... aArgs)
330 if (isSome()) {
331 aFunc(ref(), Forward<Args>(aArgs)...);
335 template<typename F, typename... Args>
336 void apply(F&& aFunc, Args&&... aArgs) const
338 if (isSome()) {
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)
350 if (isSome()) {
351 Maybe<R> val;
352 val.emplace(aFunc(ref(), Forward<Args>(aArgs)...));
353 return val;
355 return Maybe<R>();
358 template<typename R, typename... FArgs, typename... Args>
359 Maybe<R> map(R (*aFunc)(const T&, FArgs...), Args&&... aArgs) const
361 if (isSome()) {
362 Maybe<R> val;
363 val.emplace(aFunc(ref(), Forward<Args>(aArgs)...));
364 return val;
366 return Maybe<R>();
369 /* If |isSome()|, empties this Maybe and destroys its contents. */
370 void reset()
372 if (isSome()) {
373 ref().~T();
374 mIsSome = false;
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)...);
387 mIsSome = true;
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.
401 template<typename T>
402 Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type>
403 Some(T&& aValue)
405 typedef typename RemoveCV<typename RemoveReference<T>::Type>::Type U;
406 Maybe<U> value;
407 value.emplace(Forward<T>(aValue));
408 return value;
411 template<typename T>
412 Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type>
413 ToMaybe(T* aPtr)
415 if (aPtr) {
416 return Some(*aPtr);
418 return Nothing();
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()) {
430 return false;
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()) {
480 return false;
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 */