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/. */
6 
7 /* A class for optional values and in-place lazy construction. */
8 
9 #ifndef mozilla_Maybe_h
10 #define mozilla_Maybe_h
11 
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"
17 
18 #include <new>  // for placement new
19 
20 namespace mozilla {
21 
22 struct Nothing { };
23 
24 /*
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:
32  *
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!
37  *
38  *   doSomething(Nothing());      // Passes a Maybe<Foo> containing no value.
39  *   doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|.
40  *
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.
49  *
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.
56  *
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()'.
60  *
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:
65  *
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()|.
77  *
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.
81  */
82 template<class T>
83 class Maybe
84 {
85   bool mIsSome;
86   AlignedStorage2<T> mStorage;
87 
88 public:
89   typedef T ValueType;
90 
Maybe()91   Maybe() : mIsSome(false) { }
~Maybe()92   ~Maybe() { reset(); }
93 
Maybe(Nothing)94   MOZ_IMPLICIT Maybe(Nothing) : mIsSome(false) { }
95 
Maybe(const Maybe & aOther)96   Maybe(const Maybe& aOther)
97     : mIsSome(false)
98   {
99     if (aOther.mIsSome) {
100       emplace(*aOther);
101     }
102   }
103 
Maybe(Maybe && aOther)104   Maybe(Maybe&& aOther)
105     : mIsSome(false)
106   {
107     if (aOther.mIsSome) {
108       emplace(Move(*aOther));
109       aOther.reset();
110     }
111   }
112 
113   Maybe& operator=(const Maybe& aOther)
114   {
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.
120           /*
121           ref() = aOther.ref();
122           */
123           reset();
124           emplace(*aOther);
125         } else {
126           emplace(*aOther);
127         }
128       } else {
129         reset();
130       }
131     }
132     return *this;
133   }
134 
135   Maybe& operator=(Maybe&& aOther)
136   {
137     MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
138 
139     if (aOther.mIsSome) {
140       if (mIsSome) {
141         ref() = Move(aOther.ref());
142       } else {
143         emplace(Move(*aOther));
144       }
145       aOther.reset();
146     } else {
147       reset();
148     }
149 
150     return *this;
151   }
152 
153   /* Methods that check whether this Maybe contains a value */
154   explicit operator bool() const { return isSome(); }
isSome()155   bool isSome() const { return mIsSome; }
isNothing()156   bool isNothing() const { return !mIsSome; }
157 
158   /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|. */
value()159   T value() const
160   {
161     MOZ_ASSERT(mIsSome);
162     return ref();
163   }
164 
165   /*
166    * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
167    * the default value provided.
168    */
169   template<typename V>
valueOr(V && aDefault)170   T valueOr(V&& aDefault) const
171   {
172     if (isSome()) {
173       return ref();
174     }
175     return Forward<V>(aDefault);
176   }
177 
178   /*
179    * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
180    * the value returned from the function or functor provided.
181    */
182   template<typename F>
valueOrFrom(F && aFunc)183   T valueOrFrom(F&& aFunc) const
184   {
185     if (isSome()) {
186       return ref();
187     }
188     return aFunc();
189   }
190 
191   /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|. */
ptr()192   T* ptr()
193   {
194     MOZ_ASSERT(mIsSome);
195     return &ref();
196   }
197 
ptr()198   const T* ptr() const
199   {
200     MOZ_ASSERT(mIsSome);
201     return &ref();
202   }
203 
204   /*
205    * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
206    * returns the default value provided.
207    */
ptrOr(T * aDefault)208   T* ptrOr(T* aDefault)
209   {
210     if (isSome()) {
211       return ptr();
212     }
213     return aDefault;
214   }
215 
ptrOr(const T * aDefault)216   const T* ptrOr(const T* aDefault) const
217   {
218     if (isSome()) {
219       return ptr();
220     }
221     return aDefault;
222   }
223 
224   /*
225    * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
226    * returns the value returned from the function or functor provided.
227    */
228   template<typename F>
ptrOrFrom(F && aFunc)229   T* ptrOrFrom(F&& aFunc)
230   {
231     if (isSome()) {
232       return ptr();
233     }
234     return aFunc();
235   }
236 
237   template<typename F>
ptrOrFrom(F && aFunc)238   const T* ptrOrFrom(F&& aFunc) const
239   {
240     if (isSome()) {
241       return ptr();
242     }
243     return aFunc();
244   }
245 
246   T* operator->()
247   {
248     MOZ_ASSERT(mIsSome);
249     return ptr();
250   }
251 
252   const T* operator->() const
253   {
254     MOZ_ASSERT(mIsSome);
255     return ptr();
256   }
257 
258   /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
ref()259   T& ref()
260   {
261     MOZ_ASSERT(mIsSome);
262     return *mStorage.addr();
263   }
264 
ref()265   const T& ref() const
266   {
267     MOZ_ASSERT(mIsSome);
268     return *mStorage.addr();
269   }
270 
271   /*
272    * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
273    * the default value provided.
274    */
refOr(T & aDefault)275   T& refOr(T& aDefault)
276   {
277     if (isSome()) {
278       return ref();
279     }
280     return aDefault;
281   }
282 
refOr(const T & aDefault)283   const T& refOr(const T& aDefault) const
284   {
285     if (isSome()) {
286       return ref();
287     }
288     return aDefault;
289   }
290 
291   /*
292    * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
293    * value returned from the function or functor provided.
294    */
295   template<typename F>
refOrFrom(F && aFunc)296   T& refOrFrom(F&& aFunc)
297   {
298     if (isSome()) {
299       return ref();
300     }
301     return aFunc();
302   }
303 
304   template<typename F>
refOrFrom(F && aFunc)305   const T& refOrFrom(F&& aFunc) const
306   {
307     if (isSome()) {
308       return ref();
309     }
310     return aFunc();
311   }
312 
313   T& operator*()
314   {
315     MOZ_ASSERT(mIsSome);
316     return ref();
317   }
318 
319   const T& operator*() const
320   {
321     MOZ_ASSERT(mIsSome);
322     return ref();
323   }
324 
325   /* If |isSome()|, runs the provided function or functor on the contents of
326    * this Maybe. */
327   template<typename F, typename... Args>
apply(F && aFunc,Args &&...aArgs)328   void apply(F&& aFunc, Args&&... aArgs)
329   {
330     if (isSome()) {
331       aFunc(ref(), Forward<Args>(aArgs)...);
332     }
333   }
334 
335   template<typename F, typename... Args>
apply(F && aFunc,Args &&...aArgs)336   void apply(F&& aFunc, Args&&... aArgs) const
337   {
338     if (isSome()) {
339       aFunc(ref(), Forward<Args>(aArgs)...);
340     }
341   }
342 
343   /*
344    * If |isSome()|, runs the provided function and returns the result wrapped
345    * in a Maybe. If |isNothing()|, returns an empty Maybe value.
346    */
347   template<typename R, typename... FArgs, typename... Args>
map(R (* aFunc)(T &,FArgs...),Args &&...aArgs)348   Maybe<R> map(R (*aFunc)(T&, FArgs...), Args&&... aArgs)
349   {
350     if (isSome()) {
351       Maybe<R> val;
352       val.emplace(aFunc(ref(), Forward<Args>(aArgs)...));
353       return val;
354     }
355     return Maybe<R>();
356   }
357 
358   template<typename R, typename... FArgs, typename... Args>
map(R (* aFunc)(const T &,FArgs...),Args &&...aArgs)359   Maybe<R> map(R (*aFunc)(const T&, FArgs...), Args&&... aArgs) const
360   {
361     if (isSome()) {
362       Maybe<R> val;
363       val.emplace(aFunc(ref(), Forward<Args>(aArgs)...));
364       return val;
365     }
366     return Maybe<R>();
367   }
368 
369   /* If |isSome()|, empties this Maybe and destroys its contents. */
reset()370   void reset()
371   {
372     if (isSome()) {
373       ref().~T();
374       mIsSome = false;
375     }
376   }
377 
378   /*
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.
381    */
382   template<typename... Args>
emplace(Args &&...aArgs)383   void emplace(Args&&... aArgs)
384   {
385     MOZ_ASSERT(!mIsSome);
386     ::new (mStorage.addr()) T(Forward<Args>(aArgs)...);
387     mIsSome = true;
388   }
389 };
390 
391 /*
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.
394  *
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.
400  */
401 template<typename T>
402 Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type>
Some(T && aValue)403 Some(T&& aValue)
404 {
405   typedef typename RemoveCV<typename RemoveReference<T>::Type>::Type U;
406   Maybe<U> value;
407   value.emplace(Forward<T>(aValue));
408   return value;
409 }
410 
411 template<typename T>
412 Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type>
ToMaybe(T * aPtr)413 ToMaybe(T* aPtr)
414 {
415   if (aPtr) {
416     return Some(*aPtr);
417   }
418   return Nothing();
419 }
420 
421 /*
422  * Two Maybe<T> values are equal if
423  * - both are Nothing, or
424  * - both are Some, and the values they contain are equal.
425  */
426 template<typename T> bool
427 operator==(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
428 {
429   if (aLHS.isNothing() != aRHS.isNothing()) {
430     return false;
431   }
432   return aLHS.isNothing() || *aLHS == *aRHS;
433 }
434 
435 template<typename T> bool
436 operator!=(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
437 {
438   return !(aLHS == aRHS);
439 }
440 
441 /*
442  * We support comparison to Nothing to allow reasonable expressions like:
443  *   if (maybeValue == Nothing()) { ... }
444  */
445 template<typename T> bool
446 operator==(const Maybe<T>& aLHS, const Nothing& aRHS)
447 {
448   return aLHS.isNothing();
449 }
450 
451 template<typename T> bool
452 operator!=(const Maybe<T>& aLHS, const Nothing& aRHS)
453 {
454   return !(aLHS == aRHS);
455 }
456 
457 template<typename T> bool
458 operator==(const Nothing& aLHS, const Maybe<T>& aRHS)
459 {
460   return aRHS.isNothing();
461 }
462 
463 template<typename T> bool
464 operator!=(const Nothing& aLHS, const Maybe<T>& aRHS)
465 {
466   return !(aLHS == aRHS);
467 }
468 
469 /*
470  * Maybe<T> values are ordered in the same way T values are ordered, except that
471  * Nothing comes before anything else.
472  */
473 template<typename T> bool
474 operator<(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
475 {
476   if (aLHS.isNothing()) {
477     return aRHS.isSome();
478   }
479   if (aRHS.isNothing()) {
480     return false;
481   }
482   return *aLHS < *aRHS;
483 }
484 
485 template<typename T> bool
486 operator>(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
487 {
488   return !(aLHS < aRHS || aLHS == aRHS);
489 }
490 
491 template<typename T> bool
492 operator<=(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
493 {
494   return aLHS < aRHS || aLHS == aRHS;
495 }
496 
497 template<typename T> bool
498 operator>=(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
499 {
500   return !(aLHS < aRHS);
501 }
502 
503 } // namespace mozilla
504 
505 #endif /* mozilla_Maybe_h */
506