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/MemoryChecking.h"
16 #include "mozilla/Move.h"
17 #include "mozilla/OperatorNewExtensions.h"
18 #include "mozilla/Poison.h"
19 #include "mozilla/TypeTraits.h"
21 #include <new> // for placement new
23 #include <type_traits>
25 class nsCycleCollectionTraversalCallback
;
28 inline void CycleCollectionNoteChild(
29 nsCycleCollectionTraversalCallback
& aCallback
, T
* aChild
, const char* aName
,
38 // You would think that poisoning Maybe instances could just be a call
39 // to mozWritePoison. Unfortunately, using a simple call to
40 // mozWritePoison generates poor code on MSVC for small structures. The
41 // generated code contains (always not-taken) branches and does a bunch
42 // of setup for `rep stos{l,q}`, even though we know at compile time
43 // exactly how many words we're poisoning. Instead, we're going to
44 // force MSVC to generate the code we want via recursive templates.
46 // Write the given poisonValue into p at offset*sizeof(uintptr_t).
47 template <size_t offset
>
48 inline void WritePoisonAtOffset(void* p
, const uintptr_t poisonValue
) {
49 memcpy(static_cast<char*>(p
) + offset
* sizeof(poisonValue
), &poisonValue
,
53 template <size_t Offset
, size_t NOffsets
>
54 struct InlinePoisoner
{
55 static void poison(void* p
, const uintptr_t poisonValue
) {
56 WritePoisonAtOffset
<Offset
>(p
, poisonValue
);
57 InlinePoisoner
<Offset
+ 1, NOffsets
>::poison(p
, poisonValue
);
62 struct InlinePoisoner
<N
, N
> {
63 static void poison(void*, const uintptr_t) {
68 // We can't generate inline code for large structures, though, because we'll
69 // blow out recursive template instantiation limits, and the code would be
70 // bloated to boot. So provide a fallback to the out-of-line poisoner.
71 template <size_t ObjectSize
>
72 struct OutOfLinePoisoner
{
73 static void poison(void* p
, const uintptr_t) {
74 mozWritePoison(p
, ObjectSize
);
79 inline void PoisonObject(T
* p
) {
80 const uintptr_t POISON
= mozPoisonValue();
81 Conditional
<(sizeof(T
) <= 8 * sizeof(POISON
)),
82 InlinePoisoner
<0, sizeof(T
) / sizeof(POISON
)>,
83 OutOfLinePoisoner
<sizeof(T
)>>::Type::poison(p
, POISON
);
87 struct MaybePoisoner
{
88 static const size_t N
= sizeof(T
);
90 static void poison(void* aPtr
) {
91 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
92 if (N
>= sizeof(uintptr_t)) {
93 PoisonObject(static_cast<typename RemoveCV
<T
>::Type
*>(aPtr
));
96 MOZ_MAKE_MEM_UNDEFINED(aPtr
, N
);
100 } // namespace detail
103 * Maybe is a container class which contains either zero or one elements. It
104 * serves two roles. It can represent values which are *semantically* optional,
105 * augmenting a type with an explicit 'Nothing' value. In this role, it provides
106 * methods that make it easy to work with values that may be missing, along with
107 * equality and comparison operators so that Maybe values can be stored in
108 * containers. Maybe values can be constructed conveniently in expressions using
109 * type inference, as follows:
111 * void doSomething(Maybe<Foo> aFoo) {
112 * if (aFoo) // Make sure that aFoo contains a value...
113 * aFoo->takeAction(); // and then use |aFoo->| to access it.
114 * } // |*aFoo| also works!
116 * doSomething(Nothing()); // Passes a Maybe<Foo> containing no value.
117 * doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|.
119 * You'll note that it's important to check whether a Maybe contains a value
120 * before using it, using conversion to bool, |isSome()|, or |isNothing()|. You
121 * can avoid these checks, and sometimes write more readable code, using
122 * |valueOr()|, |ptrOr()|, and |refOr()|, which allow you to retrieve the value
123 * in the Maybe and provide a default for the 'Nothing' case. You can also use
124 * |apply()| to call a function only if the Maybe holds a value, and |map()| to
125 * transform the value in the Maybe, returning another Maybe with a possibly
128 * Maybe's other role is to support lazily constructing objects without using
129 * dynamic storage. A Maybe directly contains storage for a value, but it's
130 * empty by default. |emplace()|, as mentioned above, can be used to construct a
131 * value in Maybe's storage. The value a Maybe contains can be destroyed by
132 * calling |reset()|; this will happen automatically if a Maybe is destroyed
133 * while holding a value.
135 * It's a common idiom in C++ to use a pointer as a 'Maybe' type, with a null
136 * value meaning 'Nothing' and any other value meaning 'Some'. You can convert
137 * from such a pointer to a Maybe value using 'ToMaybe()'.
139 * Maybe is inspired by similar types in the standard library of many other
140 * languages (e.g. Haskell's Maybe and Rust's Option). In the C++ world it's
141 * very similar to std::optional, which was proposed for C++14 and originated in
142 * Boost. The most important differences between Maybe and std::optional are:
144 * - std::optional<T> may be compared with T. We deliberately forbid that.
145 * - std::optional allows in-place construction without a separate call to
146 * |emplace()| by using a dummy |in_place_t| value to tag the appropriate
148 * - std::optional has |valueOr()|, equivalent to Maybe's |valueOr()|, but
149 * lacks corresponding methods for |refOr()| and |ptrOr()|.
150 * - std::optional lacks |map()| and |apply()|, making it less suitable for
151 * functional-style code.
152 * - std::optional lacks many convenience functions that Maybe has. Most
153 * unfortunately, it lacks equivalents of the type-inferred constructor
154 * functions |Some()| and |Nothing()|.
157 class MOZ_NON_PARAM MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe
{
158 MOZ_ALIGNAS_IN_STRUCT(T
) unsigned char mStorage
[sizeof(T
)];
159 char mIsSome
; // not bool -- guarantees minimal space consumption
161 // GCC fails due to -Werror=strict-aliasing if |mStorage| is directly cast to
162 // T*. Indirecting through these functions addresses the problem.
163 void* data() { return mStorage
; }
164 const void* data() const { return mStorage
; }
166 void poisonData() { detail::MaybePoisoner
<T
>::poison(data()); }
171 MOZ_ALLOW_TEMPORARY
Maybe() : mIsSome(false) {}
172 ~Maybe() { reset(); }
174 MOZ_ALLOW_TEMPORARY MOZ_IMPLICIT
Maybe(Nothing
) : mIsSome(false) {}
176 Maybe(const Maybe
& aOther
) : mIsSome(false) {
177 if (aOther
.mIsSome
) {
183 * Maybe<T> can be copy-constructed from a Maybe<U> if U is convertible to T.
185 template <typename U
, typename
= typename
std::enable_if
<
186 std::is_convertible
<U
, T
>::value
>::type
>
187 MOZ_IMPLICIT
Maybe(const Maybe
<U
>& aOther
) : mIsSome(false) {
188 if (aOther
.isSome()) {
193 Maybe(Maybe
&& aOther
) : mIsSome(false) {
194 if (aOther
.mIsSome
) {
195 emplace(std::move(*aOther
));
201 * Maybe<T> can be move-constructed from a Maybe<U> if U is convertible to T.
203 template <typename U
, typename
= typename
std::enable_if
<
204 std::is_convertible
<U
, T
>::value
>::type
>
205 MOZ_IMPLICIT
Maybe(Maybe
<U
>&& aOther
) : mIsSome(false) {
206 if (aOther
.isSome()) {
207 emplace(std::move(*aOther
));
212 Maybe
& operator=(const Maybe
& aOther
) {
213 if (&aOther
!= this) {
214 if (aOther
.mIsSome
) {
216 ref() = aOther
.ref();
227 template <typename U
, typename
= typename
std::enable_if
<
228 std::is_convertible
<U
, T
>::value
>::type
>
229 Maybe
& operator=(const Maybe
<U
>& aOther
) {
230 if (aOther
.isSome()) {
232 ref() = aOther
.ref();
242 Maybe
& operator=(Maybe
&& aOther
) {
243 MOZ_ASSERT(this != &aOther
, "Self-moves are prohibited");
245 if (aOther
.mIsSome
) {
247 ref() = std::move(aOther
.ref());
249 emplace(std::move(*aOther
));
259 template <typename U
, typename
= typename
std::enable_if
<
260 std::is_convertible
<U
, T
>::value
>::type
>
261 Maybe
& operator=(Maybe
<U
>&& aOther
) {
262 if (aOther
.isSome()) {
264 ref() = std::move(aOther
.ref());
266 emplace(std::move(*aOther
));
276 /* Methods that check whether this Maybe contains a value */
277 explicit operator bool() const { return isSome(); }
278 bool isSome() const { return mIsSome
; }
279 bool isNothing() const { return !mIsSome
; }
281 /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|.
286 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
287 * the default value provided.
289 template <typename V
>
290 T
valueOr(V
&& aDefault
) const {
294 return std::forward
<V
>(aDefault
);
298 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
299 * the value returned from the function or functor provided.
301 template <typename F
>
302 T
valueOrFrom(F
&& aFunc
) const {
309 /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|.
312 const T
* ptr() const;
315 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
316 * returns the default value provided.
318 T
* ptrOr(T
* aDefault
) {
325 const T
* ptrOr(const T
* aDefault
) const {
333 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
334 * returns the value returned from the function or functor provided.
336 template <typename F
>
337 T
* ptrOrFrom(F
&& aFunc
) {
344 template <typename F
>
345 const T
* ptrOrFrom(F
&& aFunc
) const {
353 const T
* operator->() const;
355 /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
357 const T
& ref() const;
360 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
361 * the default value provided.
363 T
& refOr(T
& aDefault
) {
370 const T
& refOr(const T
& aDefault
) const {
378 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
379 * value returned from the function or functor provided.
381 template <typename F
>
382 T
& refOrFrom(F
&& aFunc
) {
389 template <typename F
>
390 const T
& refOrFrom(F
&& aFunc
) const {
398 const T
& operator*() const;
400 /* If |isSome()|, runs the provided function or functor on the contents of
402 template <typename Func
>
403 Maybe
& apply(Func
&& aFunc
) {
405 std::forward
<Func
>(aFunc
)(ref());
410 template <typename Func
>
411 const Maybe
& apply(Func
&& aFunc
) const {
413 std::forward
<Func
>(aFunc
)(ref());
419 * If |isSome()|, runs the provided function and returns the result wrapped
420 * in a Maybe. If |isNothing()|, returns an empty Maybe value with the same
421 * value type as what the provided function would have returned.
423 template <typename Func
>
424 auto map(Func
&& aFunc
) {
425 Maybe
<decltype(std::forward
<Func
>(aFunc
)(ref()))> val
;
427 val
.emplace(std::forward
<Func
>(aFunc
)(ref()));
432 template <typename Func
>
433 auto map(Func
&& aFunc
) const {
434 Maybe
<decltype(std::forward
<Func
>(aFunc
)(ref()))> val
;
436 val
.emplace(std::forward
<Func
>(aFunc
)(ref()));
441 /* If |isSome()|, empties this Maybe and destroys its contents. */
451 * Constructs a T value in-place in this empty Maybe<T>'s storage. The
452 * arguments to |emplace()| are the parameters to T's constructor.
454 template <typename
... Args
>
455 void emplace(Args
&&... aArgs
);
457 friend std::ostream
& operator<<(std::ostream
& aStream
,
458 const Maybe
<T
>& aMaybe
) {
460 aStream
<< aMaybe
.ref();
462 aStream
<< "<Nothing>";
468 template <typename T
>
469 T Maybe
<T
>::value() const {
470 MOZ_DIAGNOSTIC_ASSERT(mIsSome
);
474 template <typename T
>
476 MOZ_DIAGNOSTIC_ASSERT(mIsSome
);
480 template <typename T
>
481 const T
* Maybe
<T
>::ptr() const {
482 MOZ_DIAGNOSTIC_ASSERT(mIsSome
);
486 template <typename T
>
487 T
* Maybe
<T
>::operator->() {
488 MOZ_DIAGNOSTIC_ASSERT(mIsSome
);
492 template <typename T
>
493 const T
* Maybe
<T
>::operator->() const {
494 MOZ_DIAGNOSTIC_ASSERT(mIsSome
);
498 template <typename T
>
500 MOZ_DIAGNOSTIC_ASSERT(mIsSome
);
501 return *static_cast<T
*>(data());
504 template <typename T
>
505 const T
& Maybe
<T
>::ref() const {
506 MOZ_DIAGNOSTIC_ASSERT(mIsSome
);
507 return *static_cast<const T
*>(data());
510 template <typename T
>
511 T
& Maybe
<T
>::operator*() {
512 MOZ_DIAGNOSTIC_ASSERT(mIsSome
);
516 template <typename T
>
517 const T
& Maybe
<T
>::operator*() const {
518 MOZ_DIAGNOSTIC_ASSERT(mIsSome
);
522 template <typename T
>
523 template <typename
... Args
>
524 void Maybe
<T
>::emplace(Args
&&... aArgs
) {
525 MOZ_DIAGNOSTIC_ASSERT(!mIsSome
);
526 ::new (KnownNotNull
, data()) T(std::forward
<Args
>(aArgs
)...);
531 * Some() creates a Maybe<T> value containing the provided T value. If T has a
532 * move constructor, it's used to make this as efficient as possible.
534 * Some() selects the type of Maybe it returns by removing any const, volatile,
535 * or reference qualifiers from the type of the value you pass to it. This gives
536 * it more intuitive behavior when used in expressions, but it also means that
537 * if you need to construct a Maybe value that holds a const, volatile, or
538 * reference value, you need to use emplace() instead.
540 template <typename T
, typename U
= typename
std::remove_cv
<
541 typename
std::remove_reference
<T
>::type
>::type
>
542 Maybe
<U
> Some(T
&& aValue
) {
544 value
.emplace(std::forward
<T
>(aValue
));
548 template <typename T
>
549 Maybe
<typename RemoveCV
<typename RemoveReference
<T
>::Type
>::Type
> ToMaybe(
558 * Two Maybe<T> values are equal if
559 * - both are Nothing, or
560 * - both are Some, and the values they contain are equal.
562 template <typename T
>
563 bool operator==(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
) {
564 if (aLHS
.isNothing() != aRHS
.isNothing()) {
567 return aLHS
.isNothing() || *aLHS
== *aRHS
;
570 template <typename T
>
571 bool operator!=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
) {
572 return !(aLHS
== aRHS
);
576 * We support comparison to Nothing to allow reasonable expressions like:
577 * if (maybeValue == Nothing()) { ... }
579 template <typename T
>
580 bool operator==(const Maybe
<T
>& aLHS
, const Nothing
& aRHS
) {
581 return aLHS
.isNothing();
584 template <typename T
>
585 bool operator!=(const Maybe
<T
>& aLHS
, const Nothing
& aRHS
) {
586 return !(aLHS
== aRHS
);
589 template <typename T
>
590 bool operator==(const Nothing
& aLHS
, const Maybe
<T
>& aRHS
) {
591 return aRHS
.isNothing();
594 template <typename T
>
595 bool operator!=(const Nothing
& aLHS
, const Maybe
<T
>& aRHS
) {
596 return !(aLHS
== aRHS
);
600 * Maybe<T> values are ordered in the same way T values are ordered, except that
601 * Nothing comes before anything else.
603 template <typename T
>
604 bool operator<(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
) {
605 if (aLHS
.isNothing()) {
606 return aRHS
.isSome();
608 if (aRHS
.isNothing()) {
611 return *aLHS
< *aRHS
;
614 template <typename T
>
615 bool operator>(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
) {
616 return !(aLHS
< aRHS
|| aLHS
== aRHS
);
619 template <typename T
>
620 bool operator<=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
) {
621 return aLHS
< aRHS
|| aLHS
== aRHS
;
624 template <typename T
>
625 bool operator>=(const Maybe
<T
>& aLHS
, const Maybe
<T
>& aRHS
) {
626 return !(aLHS
< aRHS
);
629 template <typename T
>
630 inline void ImplCycleCollectionTraverse(
631 nsCycleCollectionTraversalCallback
& aCallback
, mozilla::Maybe
<T
>& aField
,
632 const char* aName
, uint32_t aFlags
= 0) {
634 ImplCycleCollectionTraverse(aCallback
, aField
.ref(), aName
, aFlags
);
638 template <typename T
>
639 inline void ImplCycleCollectionUnlink(mozilla::Maybe
<T
>& aField
) {
641 ImplCycleCollectionUnlink(aField
.ref());
645 } // namespace mozilla
647 #endif /* mozilla_Maybe_h */