webdriver: Implement Fullscreen command support (#100)
[gecko.git] / mfbt / Maybe.h
blobe701e1c7fc47769bf4ab70c5c2c8dd89ea0ac236
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
20 #include <ostream>
21 #include <type_traits>
23 namespace mozilla {
25 struct Nothing { };
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
51 * different type.
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
72 * constructor.
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.
85 template<class T>
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; }
96 public:
97 using ValueType = T;
99 Maybe() : mIsSome(false) { }
100 ~Maybe() { reset(); }
102 MOZ_IMPLICIT Maybe(Nothing) : mIsSome(false) { }
104 Maybe(const Maybe& aOther)
105 : mIsSome(false)
107 if (aOther.mIsSome) {
108 emplace(*aOther);
113 * Maybe<T> can be copy-constructed from a Maybe<U> if U is convertible to T.
115 template<typename U,
116 typename =
117 typename std::enable_if<std::is_convertible<U, T>::value>::type>
118 MOZ_IMPLICIT
119 Maybe(const Maybe<U>& aOther)
120 : mIsSome(false)
122 if (aOther.isSome()) {
123 emplace(*aOther);
127 Maybe(Maybe&& aOther)
128 : mIsSome(false)
130 if (aOther.mIsSome) {
131 emplace(Move(*aOther));
132 aOther.reset();
137 * Maybe<T> can be move-constructed from a Maybe<U> if U is convertible to T.
139 template<typename U,
140 typename =
141 typename std::enable_if<std::is_convertible<U, T>::value>::type>
142 MOZ_IMPLICIT
143 Maybe(Maybe<U>&& aOther)
144 : mIsSome(false)
146 if (aOther.isSome()) {
147 emplace(Move(*aOther));
148 aOther.reset();
152 Maybe& operator=(const Maybe& aOther)
154 if (&aOther != this) {
155 if (aOther.mIsSome) {
156 if (mIsSome) {
157 ref() = aOther.ref();
158 } else {
159 emplace(*aOther);
161 } else {
162 reset();
165 return *this;
168 template<typename U,
169 typename =
170 typename std::enable_if<std::is_convertible<U, T>::value>::type>
171 Maybe& operator=(const Maybe<U>& aOther)
173 if (aOther.isSome()) {
174 if (mIsSome) {
175 ref() = aOther.ref();
176 } else {
177 emplace(*aOther);
179 } else {
180 reset();
182 return *this;
185 Maybe& operator=(Maybe&& aOther)
187 MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
189 if (aOther.mIsSome) {
190 if (mIsSome) {
191 ref() = Move(aOther.ref());
192 } else {
193 emplace(Move(*aOther));
195 aOther.reset();
196 } else {
197 reset();
200 return *this;
203 template<typename U,
204 typename =
205 typename std::enable_if<std::is_convertible<U, T>::value>::type>
206 Maybe& operator=(Maybe<U>&& aOther)
208 if (aOther.isSome()) {
209 if (mIsSome) {
210 ref() = Move(aOther.ref());
211 } else {
212 emplace(Move(*aOther));
214 aOther.reset();
215 } else {
216 reset();
219 return *this;
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()|. */
228 T value() const
230 MOZ_ASSERT(mIsSome);
231 return ref();
235 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
236 * the default value provided.
238 template<typename V>
239 T valueOr(V&& aDefault) const
241 if (isSome()) {
242 return ref();
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.
251 template<typename F>
252 T valueOrFrom(F&& aFunc) const
254 if (isSome()) {
255 return ref();
257 return aFunc();
260 /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|. */
261 T* ptr()
263 MOZ_ASSERT(mIsSome);
264 return &ref();
267 const T* ptr() const
269 MOZ_ASSERT(mIsSome);
270 return &ref();
274 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
275 * returns the default value provided.
277 T* ptrOr(T* aDefault)
279 if (isSome()) {
280 return ptr();
282 return aDefault;
285 const T* ptrOr(const T* aDefault) const
287 if (isSome()) {
288 return ptr();
290 return aDefault;
294 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
295 * returns the value returned from the function or functor provided.
297 template<typename F>
298 T* ptrOrFrom(F&& aFunc)
300 if (isSome()) {
301 return ptr();
303 return aFunc();
306 template<typename F>
307 const T* ptrOrFrom(F&& aFunc) const
309 if (isSome()) {
310 return ptr();
312 return aFunc();
315 T* operator->()
317 MOZ_ASSERT(mIsSome);
318 return ptr();
321 const T* operator->() const
323 MOZ_ASSERT(mIsSome);
324 return ptr();
327 /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
328 T& ref()
330 MOZ_ASSERT(mIsSome);
331 return *static_cast<T*>(data());
334 const T& ref() const
336 MOZ_ASSERT(mIsSome);
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)
346 if (isSome()) {
347 return ref();
349 return aDefault;
352 const T& refOr(const T& aDefault) const
354 if (isSome()) {
355 return ref();
357 return aDefault;
361 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
362 * value returned from the function or functor provided.
364 template<typename F>
365 T& refOrFrom(F&& aFunc)
367 if (isSome()) {
368 return ref();
370 return aFunc();
373 template<typename F>
374 const T& refOrFrom(F&& aFunc) const
376 if (isSome()) {
377 return ref();
379 return aFunc();
382 T& operator*()
384 MOZ_ASSERT(mIsSome);
385 return ref();
388 const T& operator*() const
390 MOZ_ASSERT(mIsSome);
391 return ref();
394 /* If |isSome()|, runs the provided function or functor on the contents of
395 * this Maybe. */
396 template<typename Func>
397 Maybe& apply(Func aFunc)
399 if (isSome()) {
400 aFunc(ref());
402 return *this;
405 template<typename Func>
406 const Maybe& apply(Func aFunc) const
408 if (isSome()) {
409 aFunc(ref());
411 return *this;
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()));
422 if (isSome()) {
423 Maybe<ReturnType> val;
424 val.emplace(aFunc(ref()));
425 return val;
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()));
434 if (isSome()) {
435 Maybe<ReturnType> val;
436 val.emplace(aFunc(ref()));
437 return val;
439 return Maybe<ReturnType>();
442 /* If |isSome()|, empties this Maybe and destroys its contents. */
443 void reset()
445 if (isSome()) {
446 ref().T::~T();
447 mIsSome = false;
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)...);
460 mIsSome = true;
463 friend std::ostream&
464 operator<<(std::ostream& aStream, const Maybe<T>& aMaybe)
466 if (aMaybe) {
467 aStream << aMaybe.ref();
468 } else {
469 aStream << "<Nothing>";
471 return aStream;
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.
485 template<typename T,
486 typename U = typename std::remove_cv<
487 typename std::remove_reference<T>::type>::type>
488 Maybe<U>
489 Some(T&& aValue)
491 Maybe<U> value;
492 value.emplace(Forward<T>(aValue));
493 return value;
496 template<typename T>
497 Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type>
498 ToMaybe(T* aPtr)
500 if (aPtr) {
501 return Some(*aPtr);
503 return Nothing();
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()) {
515 return false;
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()) {
565 return false;
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 */