Line data Source code
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/OperatorNewExtensions.h"
17 : #include "mozilla/TypeTraits.h"
18 :
19 : #include <new> // for placement new
20 : #include <ostream>
21 : #include <type_traits>
22 :
23 : namespace mozilla {
24 :
25 : struct Nothing { };
26 :
27 : /*
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:
35 : *
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!
40 : *
41 : * doSomething(Nothing()); // Passes a Maybe<Foo> containing no value.
42 : * doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|.
43 : *
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.
52 : *
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.
59 : *
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()'.
63 : *
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:
68 : *
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()|.
80 : *
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.
84 : */
85 : template<class T>
86 : class MOZ_NON_PARAM MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe
87 : {
88 : alignas(T) unsigned char mStorage[sizeof(T)];
89 : char mIsSome; // not bool -- guarantees minimal space consumption
90 :
91 : // GCC fails due to -Werror=strict-aliasing if |mStorage| is directly cast to
92 : // T*. Indirecting through these functions addresses the problem.
93 4153974 : void* data() { return mStorage; }
94 163830 : const void* data() const { return mStorage; }
95 :
96 : public:
97 : using ValueType = T;
98 :
99 2045878 : Maybe() : mIsSome(false) { }
100 2621797 : ~Maybe() { reset(); }
101 :
102 549489 : MOZ_IMPLICIT Maybe(Nothing) : mIsSome(false) { }
103 :
104 29204 : Maybe(const Maybe& aOther)
105 29204 : : mIsSome(false)
106 : {
107 29204 : if (aOther.mIsSome) {
108 25492 : emplace(*aOther);
109 : }
110 29204 : }
111 :
112 : /**
113 : * Maybe<T> can be copy-constructed from a Maybe<U> if U is convertible to T.
114 : */
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)
121 : {
122 : if (aOther.isSome()) {
123 : emplace(*aOther);
124 : }
125 : }
126 :
127 3069 : Maybe(Maybe&& aOther)
128 3069 : : mIsSome(false)
129 : {
130 3069 : if (aOther.mIsSome) {
131 944 : emplace(Move(*aOther));
132 944 : aOther.reset();
133 : }
134 3069 : }
135 :
136 : /**
137 : * Maybe<T> can be move-constructed from a Maybe<U> if U is convertible to T.
138 : */
139 : template<typename U,
140 : typename =
141 : typename std::enable_if<std::is_convertible<U, T>::value>::type>
142 : MOZ_IMPLICIT
143 3 : Maybe(Maybe<U>&& aOther)
144 3 : : mIsSome(false)
145 : {
146 3 : if (aOther.isSome()) {
147 3 : emplace(Move(*aOther));
148 3 : aOther.reset();
149 : }
150 3 : }
151 :
152 2047 : Maybe& operator=(const Maybe& aOther)
153 : {
154 2047 : if (&aOther != this) {
155 2047 : if (aOther.mIsSome) {
156 918 : if (mIsSome) {
157 221 : ref() = aOther.ref();
158 : } else {
159 697 : emplace(*aOther);
160 : }
161 : } else {
162 1129 : reset();
163 : }
164 : }
165 2047 : return *this;
166 : }
167 :
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)
172 : {
173 : if (aOther.isSome()) {
174 : if (mIsSome) {
175 : ref() = aOther.ref();
176 : } else {
177 : emplace(*aOther);
178 : }
179 : } else {
180 : reset();
181 : }
182 : return *this;
183 : }
184 :
185 43849 : Maybe& operator=(Maybe&& aOther)
186 : {
187 43849 : MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
188 :
189 43849 : if (aOther.mIsSome) {
190 32275 : if (mIsSome) {
191 267 : ref() = Move(aOther.ref());
192 : } else {
193 32008 : emplace(Move(*aOther));
194 : }
195 32275 : aOther.reset();
196 : } else {
197 11574 : reset();
198 : }
199 :
200 43849 : return *this;
201 : }
202 :
203 : template<typename U,
204 : typename =
205 : typename std::enable_if<std::is_convertible<U, T>::value>::type>
206 0 : Maybe& operator=(Maybe<U>&& aOther)
207 : {
208 0 : if (aOther.isSome()) {
209 0 : if (mIsSome) {
210 0 : ref() = Move(aOther.ref());
211 : } else {
212 0 : emplace(Move(*aOther));
213 : }
214 0 : aOther.reset();
215 : } else {
216 0 : reset();
217 : }
218 :
219 0 : return *this;
220 : }
221 :
222 : /* Methods that check whether this Maybe contains a value */
223 726628 : explicit operator bool() const { return isSome(); }
224 3601945 : bool isSome() const { return mIsSome; }
225 43820 : bool isNothing() const { return !mIsSome; }
226 :
227 : /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|. */
228 16225 : T value() const
229 : {
230 16225 : MOZ_ASSERT(mIsSome);
231 16225 : return ref();
232 : }
233 :
234 : /*
235 : * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
236 : * the default value provided.
237 : */
238 : template<typename V>
239 3218 : T valueOr(V&& aDefault) const
240 : {
241 3218 : if (isSome()) {
242 641 : return ref();
243 : }
244 2577 : return Forward<V>(aDefault);
245 : }
246 :
247 : /*
248 : * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
249 : * the value returned from the function or functor provided.
250 : */
251 : template<typename F>
252 : T valueOrFrom(F&& aFunc) const
253 : {
254 : if (isSome()) {
255 : return ref();
256 : }
257 : return aFunc();
258 : }
259 :
260 : /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|. */
261 303296 : T* ptr()
262 : {
263 303296 : MOZ_ASSERT(mIsSome);
264 303296 : return &ref();
265 : }
266 :
267 30367 : const T* ptr() const
268 : {
269 30367 : MOZ_ASSERT(mIsSome);
270 30367 : return &ref();
271 : }
272 :
273 : /*
274 : * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
275 : * returns the default value provided.
276 : */
277 17691 : T* ptrOr(T* aDefault)
278 : {
279 17691 : if (isSome()) {
280 17275 : return ptr();
281 : }
282 416 : return aDefault;
283 : }
284 :
285 0 : const T* ptrOr(const T* aDefault) const
286 : {
287 0 : if (isSome()) {
288 0 : return ptr();
289 : }
290 0 : return aDefault;
291 : }
292 :
293 : /*
294 : * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
295 : * returns the value returned from the function or functor provided.
296 : */
297 : template<typename F>
298 : T* ptrOrFrom(F&& aFunc)
299 : {
300 : if (isSome()) {
301 : return ptr();
302 : }
303 : return aFunc();
304 : }
305 :
306 : template<typename F>
307 : const T* ptrOrFrom(F&& aFunc) const
308 : {
309 : if (isSome()) {
310 : return ptr();
311 : }
312 : return aFunc();
313 : }
314 :
315 278816 : T* operator->()
316 : {
317 278816 : MOZ_ASSERT(mIsSome);
318 278816 : return ptr();
319 : }
320 :
321 30367 : const T* operator->() const
322 : {
323 30367 : MOZ_ASSERT(mIsSome);
324 30367 : return ptr();
325 : }
326 :
327 : /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
328 2834588 : T& ref()
329 : {
330 2834588 : MOZ_ASSERT(mIsSome);
331 2834588 : return *static_cast<T*>(data());
332 : }
333 :
334 163830 : const T& ref() const
335 : {
336 163830 : MOZ_ASSERT(mIsSome);
337 163830 : return *static_cast<const T*>(data());
338 : }
339 :
340 : /*
341 : * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
342 : * the default value provided.
343 : */
344 0 : T& refOr(T& aDefault)
345 : {
346 0 : if (isSome()) {
347 0 : return ref();
348 : }
349 0 : return aDefault;
350 : }
351 :
352 0 : const T& refOr(const T& aDefault) const
353 : {
354 0 : if (isSome()) {
355 0 : return ref();
356 : }
357 0 : return aDefault;
358 : }
359 :
360 : /*
361 : * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
362 : * value returned from the function or functor provided.
363 : */
364 : template<typename F>
365 : T& refOrFrom(F&& aFunc)
366 : {
367 : if (isSome()) {
368 : return ref();
369 : }
370 : return aFunc();
371 : }
372 :
373 : template<typename F>
374 : const T& refOrFrom(F&& aFunc) const
375 : {
376 : if (isSome()) {
377 : return ref();
378 : }
379 : return aFunc();
380 : }
381 :
382 359671 : T& operator*()
383 : {
384 359671 : MOZ_ASSERT(mIsSome);
385 359671 : return ref();
386 : }
387 :
388 90527 : const T& operator*() const
389 : {
390 90527 : MOZ_ASSERT(mIsSome);
391 90527 : return ref();
392 : }
393 :
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)
398 : {
399 : if (isSome()) {
400 : aFunc(ref());
401 : }
402 : return *this;
403 : }
404 :
405 : template<typename Func>
406 : const Maybe& apply(Func aFunc) const
407 : {
408 : if (isSome()) {
409 : aFunc(ref());
410 : }
411 : return *this;
412 : }
413 :
414 : /*
415 : * If |isSome()|, runs the provided function and returns the result wrapped
416 : * in a Maybe. If |isNothing()|, returns an empty Maybe value.
417 : */
418 : template<typename Func>
419 0 : auto map(Func aFunc) -> Maybe<decltype(aFunc(DeclVal<Maybe<T>>().ref()))>
420 : {
421 : using ReturnType = decltype(aFunc(ref()));
422 0 : if (isSome()) {
423 0 : Maybe<ReturnType> val;
424 0 : val.emplace(aFunc(ref()));
425 0 : return val;
426 : }
427 0 : return Maybe<ReturnType>();
428 : }
429 :
430 : template<typename Func>
431 371 : auto map(Func aFunc) const -> Maybe<decltype(aFunc(DeclVal<Maybe<T>>().ref()))>
432 : {
433 : using ReturnType = decltype(aFunc(ref()));
434 371 : if (isSome()) {
435 202 : Maybe<ReturnType> val;
436 101 : val.emplace(aFunc(ref()));
437 101 : return val;
438 : }
439 270 : return Maybe<ReturnType>();
440 : }
441 :
442 : /* If |isSome()|, empties this Maybe and destroys its contents. */
443 2707551 : void reset()
444 : {
445 2707551 : if (isSome()) {
446 1319074 : ref().T::~T();
447 1319074 : mIsSome = false;
448 : }
449 2707554 : }
450 :
451 : /*
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.
454 : */
455 : template<typename... Args>
456 1319706 : void emplace(Args&&... aArgs)
457 : {
458 1319706 : MOZ_ASSERT(!mIsSome);
459 1320863 : ::new (KnownNotNull, data()) T(Forward<Args>(aArgs)...);
460 1319725 : mIsSome = true;
461 1319725 : }
462 :
463 : friend std::ostream&
464 : operator<<(std::ostream& aStream, const Maybe<T>& aMaybe)
465 : {
466 : if (aMaybe) {
467 : aStream << aMaybe.ref();
468 : } else {
469 : aStream << "<Nothing>";
470 : }
471 : return aStream;
472 : }
473 : };
474 :
475 : /*
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.
478 : *
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.
484 : */
485 : template<typename T,
486 : typename U = typename std::remove_cv<
487 : typename std::remove_reference<T>::type>::type>
488 : Maybe<U>
489 143673 : Some(T&& aValue)
490 : {
491 143673 : Maybe<U> value;
492 143673 : value.emplace(Forward<T>(aValue));
493 143673 : return value;
494 : }
495 :
496 : template<typename T>
497 : Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type>
498 : ToMaybe(T* aPtr)
499 : {
500 : if (aPtr) {
501 : return Some(*aPtr);
502 : }
503 : return Nothing();
504 : }
505 :
506 : /*
507 : * Two Maybe<T> values are equal if
508 : * - both are Nothing, or
509 : * - both are Some, and the values they contain are equal.
510 : */
511 : template<typename T> bool
512 1160 : operator==(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
513 : {
514 1160 : if (aLHS.isNothing() != aRHS.isNothing()) {
515 23 : return false;
516 : }
517 1137 : return aLHS.isNothing() || *aLHS == *aRHS;
518 : }
519 :
520 : template<typename T> bool
521 156 : operator!=(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
522 : {
523 156 : return !(aLHS == aRHS);
524 : }
525 :
526 : /*
527 : * We support comparison to Nothing to allow reasonable expressions like:
528 : * if (maybeValue == Nothing()) { ... }
529 : */
530 : template<typename T> bool
531 : operator==(const Maybe<T>& aLHS, const Nothing& aRHS)
532 : {
533 : return aLHS.isNothing();
534 : }
535 :
536 : template<typename T> bool
537 : operator!=(const Maybe<T>& aLHS, const Nothing& aRHS)
538 : {
539 : return !(aLHS == aRHS);
540 : }
541 :
542 : template<typename T> bool
543 : operator==(const Nothing& aLHS, const Maybe<T>& aRHS)
544 : {
545 : return aRHS.isNothing();
546 : }
547 :
548 : template<typename T> bool
549 : operator!=(const Nothing& aLHS, const Maybe<T>& aRHS)
550 : {
551 : return !(aLHS == aRHS);
552 : }
553 :
554 : /*
555 : * Maybe<T> values are ordered in the same way T values are ordered, except that
556 : * Nothing comes before anything else.
557 : */
558 : template<typename T> bool
559 0 : operator<(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
560 : {
561 0 : if (aLHS.isNothing()) {
562 0 : return aRHS.isSome();
563 : }
564 0 : if (aRHS.isNothing()) {
565 0 : return false;
566 : }
567 0 : return *aLHS < *aRHS;
568 : }
569 :
570 : template<typename T> bool
571 : operator>(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
572 : {
573 : return !(aLHS < aRHS || aLHS == aRHS);
574 : }
575 :
576 : template<typename T> bool
577 : operator<=(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
578 : {
579 : return aLHS < aRHS || aLHS == aRHS;
580 : }
581 :
582 : template<typename T> bool
583 : operator>=(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
584 : {
585 : return !(aLHS < aRHS);
586 : }
587 :
588 : } // namespace mozilla
589 :
590 : #endif /* mozilla_Maybe_h */
|