Bug 1066410 - Serious color distortion happened when plays WebRTC. r=jesup, a=bajaj
[gecko.git] / mfbt / Maybe.h
blob316343bc70e2c73b6c9112ccacd5d0756d65f916
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 typedef void (Maybe::* ConvertibleToBool)(float*****, double*****);
86 void nonNull(float*****, double*****) {}
88 bool mIsSome;
89 AlignedStorage2<T> mStorage;
91 public:
92 typedef T ValueType;
94 Maybe() : mIsSome(false) { }
95 ~Maybe() { reset(); }
97 MOZ_IMPLICIT Maybe(Nothing) : mIsSome(false) { }
99 Maybe(const Maybe& aOther)
100 : mIsSome(false)
102 if (aOther.mIsSome) {
103 emplace(*aOther);
107 Maybe(Maybe&& aOther)
108 : mIsSome(false)
110 if (aOther.mIsSome) {
111 emplace(Move(*aOther));
112 aOther.reset();
116 Maybe& operator=(const Maybe& aOther)
118 if (&aOther != this) {
119 if (aOther.mIsSome) {
120 if (mIsSome) {
121 // XXX(seth): The correct code for this branch, below, can't be used
122 // due to a bug in Visual Studio 2010. See bug 1052940.
124 ref() = aOther.ref();
126 reset();
127 emplace(*aOther);
128 } else {
129 emplace(*aOther);
131 } else {
132 reset();
135 return *this;
138 Maybe& operator=(Maybe&& aOther)
140 MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
142 if (aOther.mIsSome) {
143 if (mIsSome) {
144 ref() = Move(aOther.ref());
145 } else {
146 emplace(Move(*aOther));
148 aOther.reset();
149 } else {
150 reset();
153 return *this;
156 /* Methods that check whether this Maybe contains a value */
157 operator ConvertibleToBool() const { return mIsSome ? &Maybe::nonNull : 0; }
158 bool isSome() const { return mIsSome; }
159 bool isNothing() const { return !mIsSome; }
161 /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|. */
162 T value() const
164 MOZ_ASSERT(mIsSome);
165 return ref();
169 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
170 * the default value provided.
172 template<typename V>
173 T valueOr(V&& aDefault) const
175 if (isSome()) {
176 return ref();
178 return Forward<V>(aDefault);
182 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
183 * the value returned from the function or functor provided.
185 template<typename F>
186 T valueOrFrom(F&& aFunc) const
188 if (isSome()) {
189 return ref();
191 return aFunc();
194 /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|. */
195 T* ptr()
197 MOZ_ASSERT(mIsSome);
198 return &ref();
201 const T* ptr() const
203 MOZ_ASSERT(mIsSome);
204 return &ref();
208 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
209 * returns the default value provided.
211 T* ptrOr(T* aDefault)
213 if (isSome()) {
214 return ptr();
216 return aDefault;
219 const T* ptrOr(const T* aDefault) const
221 if (isSome()) {
222 return ptr();
224 return aDefault;
228 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
229 * returns the value returned from the function or functor provided.
231 template<typename F>
232 T* ptrOrFrom(F&& aFunc)
234 if (isSome()) {
235 return ptr();
237 return aFunc();
240 template<typename F>
241 const T* ptrOrFrom(F&& aFunc) const
243 if (isSome()) {
244 return ptr();
246 return aFunc();
249 T* operator->()
251 MOZ_ASSERT(mIsSome);
252 return ptr();
255 const T* operator->() const
257 MOZ_ASSERT(mIsSome);
258 return ptr();
261 /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
262 T& ref()
264 MOZ_ASSERT(mIsSome);
265 return *mStorage.addr();
268 const T& ref() const
270 MOZ_ASSERT(mIsSome);
271 return *mStorage.addr();
275 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
276 * the default value provided.
278 T& refOr(T& aDefault)
280 if (isSome()) {
281 return ref();
283 return aDefault;
286 const T& refOr(const T& aDefault) const
288 if (isSome()) {
289 return ref();
291 return aDefault;
295 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
296 * value returned from the function or functor provided.
298 template<typename F>
299 T& refOrFrom(F&& aFunc)
301 if (isSome()) {
302 return ref();
304 return aFunc();
307 template<typename F>
308 const T& refOrFrom(F&& aFunc) const
310 if (isSome()) {
311 return ref();
313 return aFunc();
316 T& operator*()
318 MOZ_ASSERT(mIsSome);
319 return ref();
322 const T& operator*() const
324 MOZ_ASSERT(mIsSome);
325 return ref();
328 /* If |isSome()|, runs the provided function or functor on the contents of
329 * this Maybe. */
330 template<typename F>
331 void apply(F&& aFunc)
333 if (isSome()) {
334 aFunc(ref());
338 template<typename F>
339 void apply(F&& aFunc) const
341 if (isSome()) {
342 aFunc(ref());
346 /* Variant of |apply()| that takes an additional argument for the function. */
347 template<typename F, typename A>
348 void apply(F&& aFunc, A&& aArg)
350 if (isSome()) {
351 aFunc(ref(), Forward<A>(aArg));
355 template<typename F, typename A>
356 void apply(F&& aFunc, A&& aArg) const
358 if (isSome()) {
359 aFunc(ref(), Forward<A>(aArg));
364 * If |isSome()|, runs the provided function and returns the result wrapped
365 * in a Maybe. If |isNothing()|, returns an empty Maybe value.
367 template<typename R>
368 Maybe<R> map(R(*aFunc)(T&))
370 if (isSome()) {
371 Maybe<R> val;
372 val.emplace(aFunc(ref()));
373 return val;
375 return Maybe<R>();
378 template<typename R>
379 Maybe<R> map(R(*aFunc)(const T&)) const
381 if (isSome()) {
382 Maybe<R> val;
383 val.emplace(aFunc(ref()));
384 return val;
386 return Maybe<R>();
389 /* Variant of |map()| that takes an additional argument for the function. */
390 template<typename R, typename FA, typename A>
391 Maybe<R> map(R(*aFunc)(T&, FA), A&& aArg)
393 if (isSome()) {
394 Maybe<R> val;
395 val.emplace(aFunc(ref(), Forward<A>(aArg)));
396 return val;
398 return Maybe<R>();
401 template<typename R, typename FA, typename A>
402 Maybe<R> map(R(*aFunc)(const T&, FA), A&& aArg) const
404 if (isSome()) {
405 Maybe<R> val;
406 val.emplace(aFunc(ref(), Forward<A>(aArg)));
407 return val;
409 return Maybe<R>();
412 /* If |isSome()|, empties this Maybe and destroys its contents. */
413 void reset()
415 if (isSome()) {
416 ref().~T();
417 mIsSome = false;
422 * Constructs a T value in-place in this empty Maybe<T>'s storage. The
423 * arguments to |emplace()| are the parameters to T's constructor.
425 * WARNING: You can't pass a literal nullptr to these methods without
426 * hitting GCC 4.4-only (and hence B2G-only) compile errors.
428 void emplace()
430 MOZ_ASSERT(!mIsSome);
431 ::new (mStorage.addr()) T();
432 mIsSome = true;
435 template<typename T1>
436 void emplace(T1&& t1)
438 MOZ_ASSERT(!mIsSome);
439 ::new (mStorage.addr()) T(Forward<T1>(t1));
440 mIsSome = true;
443 template<typename T1, typename T2>
444 void emplace(T1&& t1, T2&& t2)
446 MOZ_ASSERT(!mIsSome);
447 ::new (mStorage.addr()) T(Forward<T1>(t1), Forward<T2>(t2));
448 mIsSome = true;
451 template<typename T1, typename T2, typename T3>
452 void emplace(T1&& t1, T2&& t2, T3&& t3)
454 MOZ_ASSERT(!mIsSome);
455 ::new (mStorage.addr()) T(Forward<T1>(t1), Forward<T2>(t2), Forward<T3>(t3));
456 mIsSome = true;
459 template<typename T1, typename T2, typename T3, typename T4>
460 void emplace(T1&& t1, T2&& t2, T3&& t3, T4&& t4)
462 MOZ_ASSERT(!mIsSome);
463 ::new (mStorage.addr()) T(Forward<T1>(t1), Forward<T2>(t2), Forward<T3>(t3),
464 Forward<T4>(t4));
465 mIsSome = true;
468 template<typename T1, typename T2, typename T3, typename T4, typename T5>
469 void emplace(T1&& t1, T2&& t2, T3&& t3, T4&& t4, T5&& t5)
471 MOZ_ASSERT(!mIsSome);
472 ::new (mStorage.addr()) T(Forward<T1>(t1), Forward<T2>(t2), Forward<T3>(t3),
473 Forward<T4>(t4), Forward<T5>(t5));
474 mIsSome = true;
477 template<typename T1, typename T2, typename T3, typename T4, typename T5,
478 typename T6>
479 void emplace(T1&& t1, T2&& t2, T3&& t3, T4&& t4, T5&& t5, T6&& t6)
481 MOZ_ASSERT(!mIsSome);
482 ::new (mStorage.addr()) T(Forward<T1>(t1), Forward<T2>(t2), Forward<T3>(t3),
483 Forward<T4>(t4), Forward<T5>(t5), Forward<T6>(t6));
484 mIsSome = true;
487 template<typename T1, typename T2, typename T3, typename T4, typename T5,
488 typename T6, typename T7>
489 void emplace(T1&& t1, T2&& t2, T3&& t3, T4&& t4, T5&& t5, T6&& t6,
490 T7&& t7)
492 MOZ_ASSERT(!mIsSome);
493 ::new (mStorage.addr()) T(Forward<T1>(t1), Forward<T2>(t2), Forward<T3>(t3),
494 Forward<T4>(t4), Forward<T5>(t5), Forward<T6>(t6),
495 Forward<T7>(t7));
496 mIsSome = true;
499 template<typename T1, typename T2, typename T3, typename T4, typename T5,
500 typename T6, typename T7, typename T8>
501 void emplace(T1&& t1, T2&& t2, T3&& t3, T4&& t4, T5&& t5, T6&& t6,
502 T7&& t7, T8&& t8)
504 MOZ_ASSERT(!mIsSome);
505 ::new (mStorage.addr()) T(Forward<T1>(t1), Forward<T2>(t2), Forward<T3>(t3),
506 Forward<T4>(t4), Forward<T5>(t5), Forward<T6>(t6),
507 Forward<T7>(t7), Forward<T8>(t8));
508 mIsSome = true;
511 template<typename T1, typename T2, typename T3, typename T4, typename T5,
512 typename T6, typename T7, typename T8, typename T9>
513 void emplace(T1&& t1, T2&& t2, T3&& t3, T4&& t4, T5&& t5, T6&& t6,
514 T7&& t7, T8&& t8, T9&& t9)
516 MOZ_ASSERT(!mIsSome);
517 ::new (mStorage.addr()) T(Forward<T1>(t1), Forward<T2>(t2), Forward<T3>(t3),
518 Forward<T4>(t4), Forward<T5>(t5), Forward<T6>(t6),
519 Forward<T7>(t7), Forward<T8>(t8), Forward<T9>(t9));
520 mIsSome = true;
523 template<typename T1, typename T2, typename T3, typename T4, typename T5,
524 typename T6, typename T7, typename T8, typename T9, typename T10>
525 void emplace(T1&& t1, T2&& t2, T3&& t3, T4&& t4, T5&& t5, T6&& t6,
526 T7&& t7, T8&& t8, T9&& t9, T10&& t10)
528 MOZ_ASSERT(!mIsSome);
529 ::new (mStorage.addr()) T(Forward<T1>(t1), Forward<T2>(t2), Forward<T3>(t3),
530 Forward<T4>(t4), Forward<T5>(t5), Forward<T6>(t6),
531 Forward<T7>(t7), Forward<T8>(t8), Forward<T9>(t9),
532 Forward<T1>(t10));
533 mIsSome = true;
538 * Some() creates a Maybe<T> value containing the provided T value. If T has a
539 * move constructor, it's used to make this as efficient as possible.
541 * Some() selects the type of Maybe it returns by removing any const, volatile,
542 * or reference qualifiers from the type of the value you pass to it. This gives
543 * it more intuitive behavior when used in expressions, but it also means that
544 * if you need to construct a Maybe value that holds a const, volatile, or
545 * reference value, you need to use emplace() instead.
547 template<typename T>
548 Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type>
549 Some(T&& aValue)
551 typedef typename RemoveCV<typename RemoveReference<T>::Type>::Type U;
552 Maybe<U> value;
553 value.emplace(Forward<T>(aValue));
554 return value;
557 template<typename T>
558 Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type>
559 ToMaybe(T* aPtr)
561 if (aPtr) {
562 return Some(*aPtr);
564 return Nothing();
568 * Two Maybe<T> values are equal if
569 * - both are Nothing, or
570 * - both are Some, and the values they contain are equal.
572 template<typename T> bool
573 operator==(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
575 if (aLHS.isNothing() != aRHS.isNothing()) {
576 return false;
578 return aLHS.isNothing() || *aLHS == *aRHS;
581 template<typename T> bool
582 operator!=(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
584 return !(aLHS == aRHS);
588 * We support comparison to Nothing to allow reasonable expressions like:
589 * if (maybeValue == Nothing()) { ... }
591 template<typename T> bool
592 operator==(const Maybe<T>& aLHS, const Nothing& aRHS)
594 return aLHS.isNothing();
597 template<typename T> bool
598 operator!=(const Maybe<T>& aLHS, const Nothing& aRHS)
600 return !(aLHS == aRHS);
603 template<typename T> bool
604 operator==(const Nothing& aLHS, const Maybe<T>& aRHS)
606 return aRHS.isNothing();
609 template<typename T> bool
610 operator!=(const Nothing& aLHS, const Maybe<T>& aRHS)
612 return !(aLHS == aRHS);
616 * Maybe<T> values are ordered in the same way T values are ordered, except that
617 * Nothing comes before anything else.
619 template<typename T> bool
620 operator<(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
622 if (aLHS.isNothing()) {
623 return aRHS.isSome();
625 if (aRHS.isNothing()) {
626 return false;
628 return *aLHS < *aRHS;
631 template<typename T> bool
632 operator>(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
634 return !(aLHS < aRHS || aLHS == aRHS);
637 template<typename T> bool
638 operator<=(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
640 return aLHS < aRHS || aLHS == aRHS;
643 template<typename T> bool
644 operator>=(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
646 return !(aLHS < aRHS);
649 } // namespace mozilla
651 #endif /* mozilla_Maybe_h */