Maybe.h (36308B)
1 /* -*- Mode: C++; tab-width: 2; 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 <functional> 13 #include <new> // for placement new 14 #include <ostream> 15 #include <type_traits> 16 #include <utility> 17 18 #include "mozilla/Assertions.h" 19 #include "mozilla/Attributes.h" 20 #include "mozilla/MaybeStorageBase.h" 21 #include "mozilla/MemoryChecking.h" 22 #include "mozilla/OperatorNewExtensions.h" 23 #include "mozilla/Poison.h" 24 #include "mozilla/ThreadSafety.h" 25 26 class nsCycleCollectionTraversalCallback; 27 28 template <typename T> 29 inline void CycleCollectionNoteChild( 30 nsCycleCollectionTraversalCallback& aCallback, T* aChild, const char* aName, 31 uint32_t aFlags); 32 33 namespace mozilla { 34 35 struct Nothing {}; 36 37 constexpr bool operator==(const Nothing&, const Nothing&) { return true; } 38 39 template <class T> 40 class Maybe; 41 42 namespace detail { 43 44 // You would think that poisoning Maybe instances could just be a call 45 // to mozWritePoison. Unfortunately, using a simple call to 46 // mozWritePoison generates poor code on MSVC for small structures. The 47 // generated code contains (always not-taken) branches and does a bunch 48 // of setup for `rep stos{l,q}`, even though we know at compile time 49 // exactly how many words we're poisoning. Instead, we're going to 50 // force MSVC to generate the code we want via recursive templates. 51 52 // Write the given poisonValue into p at offset*sizeof(uintptr_t). 53 template <size_t offset> 54 inline void WritePoisonAtOffset(void* p, const uintptr_t poisonValue) { 55 memcpy(static_cast<char*>(p) + offset * sizeof(poisonValue), &poisonValue, 56 sizeof(poisonValue)); 57 } 58 59 template <size_t Offset, size_t NOffsets> 60 struct InlinePoisoner { 61 static void poison(void* p, const uintptr_t poisonValue) { 62 WritePoisonAtOffset<Offset>(p, poisonValue); 63 InlinePoisoner<Offset + 1, NOffsets>::poison(p, poisonValue); 64 } 65 }; 66 67 template <size_t N> 68 struct InlinePoisoner<N, N> { 69 static void poison(void*, const uintptr_t) { 70 // All done! 71 } 72 }; 73 74 // We can't generate inline code for large structures, though, because we'll 75 // blow out recursive template instantiation limits, and the code would be 76 // bloated to boot. So provide a fallback to the out-of-line poisoner. 77 template <size_t ObjectSize> 78 struct OutOfLinePoisoner { 79 static MOZ_NEVER_INLINE void poison(void* p, const uintptr_t) { 80 mozWritePoison(p, ObjectSize); 81 } 82 }; 83 84 template <typename T> 85 inline void PoisonObject(T* p) { 86 const uintptr_t POISON = mozPoisonValue(); 87 std::conditional_t<(sizeof(T) <= 8 * sizeof(POISON)), 88 InlinePoisoner<0, sizeof(T) / sizeof(POISON)>, 89 OutOfLinePoisoner<sizeof(T)>>::poison(p, POISON); 90 } 91 92 template <typename T> 93 struct MaybePoisoner { 94 static const size_t N = sizeof(T); 95 96 static void poison(void* aPtr) { 97 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 98 if (N >= sizeof(uintptr_t)) { 99 PoisonObject(static_cast<std::remove_cv_t<T>*>(aPtr)); 100 } 101 #endif 102 MOZ_MAKE_MEM_UNDEFINED(aPtr, N); 103 } 104 }; 105 106 template <typename T, 107 bool TriviallyDestructibleAndCopyable = 108 IsTriviallyDestructibleAndCopyable<T>, 109 bool Copyable = std::is_copy_constructible_v<T>, 110 bool Movable = std::is_move_constructible_v<T>> 111 class Maybe_CopyMove_Enabler; 112 113 #define MOZ_MAYBE_COPY_OPS() \ 114 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler& aOther) { \ 115 if (downcast(aOther).isSome()) { \ 116 downcast(*this).emplace(*downcast(aOther)); \ 117 } \ 118 } \ 119 \ 120 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler& aOther) { \ 121 return downcast(*this).template operator= <T>(downcast(aOther)); \ 122 } 123 124 #define MOZ_MAYBE_MOVE_OPS() \ 125 constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) { \ 126 if (downcast(aOther).isSome()) { \ 127 downcast(*this).emplace(std::move(*downcast(aOther))); \ 128 downcast(aOther).reset(); \ 129 } \ 130 } \ 131 \ 132 constexpr Maybe_CopyMove_Enabler& operator=( \ 133 Maybe_CopyMove_Enabler&& aOther) { \ 134 downcast(*this).template operator= <T>(std::move(downcast(aOther))); \ 135 \ 136 return *this; \ 137 } 138 139 #define MOZ_MAYBE_DOWNCAST() \ 140 static constexpr Maybe<T>& downcast(Maybe_CopyMove_Enabler& aObj) { \ 141 return static_cast<Maybe<T>&>(aObj); \ 142 } \ 143 static constexpr const Maybe<T>& downcast( \ 144 const Maybe_CopyMove_Enabler& aObj) { \ 145 return static_cast<const Maybe<T>&>(aObj); \ 146 } 147 148 template <typename T> 149 class Maybe_CopyMove_Enabler<T, true, true, true> { 150 public: 151 Maybe_CopyMove_Enabler() = default; 152 153 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = default; 154 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = default; 155 constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) { 156 downcast(aOther).reset(); 157 } 158 constexpr Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&& aOther) { 159 downcast(aOther).reset(); 160 return *this; 161 } 162 163 private: 164 MOZ_MAYBE_DOWNCAST() 165 }; 166 167 template <typename T> 168 class Maybe_CopyMove_Enabler<T, true, false, true> { 169 public: 170 Maybe_CopyMove_Enabler() = default; 171 172 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete; 173 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete; 174 constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) { 175 downcast(aOther).reset(); 176 } 177 constexpr Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&& aOther) { 178 downcast(aOther).reset(); 179 return *this; 180 } 181 182 private: 183 MOZ_MAYBE_DOWNCAST() 184 }; 185 186 template <typename T> 187 class Maybe_CopyMove_Enabler<T, false, true, true> { 188 public: 189 Maybe_CopyMove_Enabler() = default; 190 191 MOZ_MAYBE_COPY_OPS() 192 MOZ_MAYBE_MOVE_OPS() 193 194 private: 195 MOZ_MAYBE_DOWNCAST() 196 }; 197 198 template <typename T> 199 class Maybe_CopyMove_Enabler<T, false, false, true> { 200 public: 201 Maybe_CopyMove_Enabler() = default; 202 203 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete; 204 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete; 205 MOZ_MAYBE_MOVE_OPS() 206 207 private: 208 MOZ_MAYBE_DOWNCAST() 209 }; 210 211 template <typename T> 212 class Maybe_CopyMove_Enabler<T, false, true, false> { 213 public: 214 Maybe_CopyMove_Enabler() = default; 215 216 MOZ_MAYBE_COPY_OPS() 217 Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&&) = delete; 218 Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&&) = delete; 219 220 private: 221 MOZ_MAYBE_DOWNCAST() 222 }; 223 224 template <typename T, bool TriviallyDestructibleAndCopyable> 225 class Maybe_CopyMove_Enabler<T, TriviallyDestructibleAndCopyable, false, 226 false> { 227 public: 228 Maybe_CopyMove_Enabler() = default; 229 230 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete; 231 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete; 232 Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&&) = delete; 233 Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&&) = delete; 234 }; 235 236 #undef MOZ_MAYBE_COPY_OPS 237 #undef MOZ_MAYBE_MOVE_OPS 238 #undef MOZ_MAYBE_DOWNCAST 239 240 template <typename T, bool TriviallyDestructibleAndCopyable = 241 IsTriviallyDestructibleAndCopyable<T>> 242 struct MaybeStorage; 243 244 template <typename T> 245 struct MaybeStorage<T, false> : MaybeStorageBase<T> { 246 protected: 247 char mIsSome = false; // not bool -- guarantees minimal space consumption 248 249 constexpr MaybeStorage() = default; 250 explicit MaybeStorage(const T& aVal) 251 : MaybeStorageBase<T>{aVal}, mIsSome{true} {} 252 explicit MaybeStorage(T&& aVal) 253 : MaybeStorageBase<T>{std::move(aVal)}, mIsSome{true} {} 254 255 template <typename... Args> 256 explicit MaybeStorage(std::in_place_t, Args&&... aArgs) 257 : MaybeStorageBase<T>{std::in_place, std::forward<Args>(aArgs)...}, 258 mIsSome{true} {} 259 260 public: 261 // Copy and move operations are no-ops, since copying is moving is implemented 262 // by Maybe_CopyMove_Enabler. 263 264 MaybeStorage(const MaybeStorage&) : MaybeStorageBase<T>{} {} 265 MaybeStorage& operator=(const MaybeStorage&) { return *this; } 266 MaybeStorage(MaybeStorage&&) : MaybeStorageBase<T>{} {} 267 MaybeStorage& operator=(MaybeStorage&&) { return *this; } 268 269 ~MaybeStorage() { 270 if (mIsSome) { 271 this->addr()->T::~T(); 272 } 273 } 274 }; 275 276 template <typename T> 277 struct MaybeStorage<T, true> : MaybeStorageBase<T> { 278 protected: 279 char mIsSome = false; // not bool -- guarantees minimal space consumption 280 281 constexpr MaybeStorage() = default; 282 constexpr explicit MaybeStorage(const T& aVal) 283 : MaybeStorageBase<T>{aVal}, mIsSome{true} {} 284 constexpr explicit MaybeStorage(T&& aVal) 285 : MaybeStorageBase<T>{std::move(aVal)}, mIsSome{true} {} 286 287 template <typename... Args> 288 constexpr explicit MaybeStorage(std::in_place_t, Args&&... aArgs) 289 : MaybeStorageBase<T>{std::in_place, std::forward<Args>(aArgs)...}, 290 mIsSome{true} {} 291 }; 292 293 template <typename T> 294 struct IsMaybeImpl : std::false_type {}; 295 296 template <typename T> 297 struct IsMaybeImpl<Maybe<T>> : std::true_type {}; 298 299 template <typename T> 300 using IsMaybe = IsMaybeImpl<std::decay_t<T>>; 301 302 } // namespace detail 303 304 template <typename T, typename U = std::remove_cv_t<std::remove_reference_t<T>>> 305 constexpr Maybe<U> Some(T&& aValue); 306 307 /* 308 * Maybe is a container class which contains either zero or one elements. It 309 * serves two roles. It can represent values which are *semantically* optional, 310 * augmenting a type with an explicit 'Nothing' value. In this role, it provides 311 * methods that make it easy to work with values that may be missing, along with 312 * equality and comparison operators so that Maybe values can be stored in 313 * containers. Maybe values can be constructed conveniently in expressions using 314 * type inference, as follows: 315 * 316 * void doSomething(Maybe<Foo> aFoo) { 317 * if (aFoo) // Make sure that aFoo contains a value... 318 * aFoo->takeAction(); // and then use |aFoo->| to access it. 319 * } // |*aFoo| also works! 320 * 321 * doSomething(Nothing()); // Passes a Maybe<Foo> containing no value. 322 * doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|. 323 * 324 * You'll note that it's important to check whether a Maybe contains a value 325 * before using it, using conversion to bool, |isSome()|, or |isNothing()|. You 326 * can avoid these checks, and sometimes write more readable code, using 327 * |valueOr()|, |ptrOr()|, and |refOr()|, which allow you to retrieve the value 328 * in the Maybe and provide a default for the 'Nothing' case. You can also use 329 * |apply()| to call a function only if the Maybe holds a value, and |map()| to 330 * transform the value in the Maybe, returning another Maybe with a possibly 331 * different type. 332 * 333 * Maybe's other role is to support lazily constructing objects without using 334 * dynamic storage. A Maybe directly contains storage for a value, but it's 335 * empty by default. |emplace()|, as mentioned above, can be used to construct a 336 * value in Maybe's storage. The value a Maybe contains can be destroyed by 337 * calling |reset()|; this will happen automatically if a Maybe is destroyed 338 * while holding a value. 339 * 340 * It's a common idiom in C++ to use a pointer as a 'Maybe' type, with a null 341 * value meaning 'Nothing' and any other value meaning 'Some'. You can convert 342 * from such a pointer to a Maybe value using 'ToMaybe()'. 343 * 344 * Maybe is inspired by similar types in the standard library of many other 345 * languages (e.g. Haskell's Maybe and Rust's Option). In the C++ world it's 346 * very similar to std::optional, which was proposed for C++14 and originated in 347 * Boost. The most important differences between Maybe and std::optional are: 348 * 349 * - std::optional<T> may be compared with T. We deliberately forbid that. 350 * - std::optional has |valueOr()|, equivalent to Maybe's |valueOr()|, but 351 * lacks corresponding methods for |refOr()| and |ptrOr()|. 352 * - std::optional lacks |map()| and |apply()|, making it less suitable for 353 * functional-style code. 354 * - std::optional lacks many convenience functions that Maybe has. Most 355 * unfortunately, it lacks equivalents of the type-inferred constructor 356 * functions |Some()| and |Nothing()|. 357 */ 358 template <class T> 359 class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_GSL_OWNER Maybe 360 : private detail::MaybeStorage<T>, 361 public detail::Maybe_CopyMove_Enabler<T> { 362 template <typename, bool, bool, bool> 363 friend class detail::Maybe_CopyMove_Enabler; 364 365 template <typename U, typename V> 366 friend constexpr Maybe<V> Some(U&& aValue); 367 368 struct SomeGuard {}; 369 370 template <typename U> 371 constexpr Maybe(U&& aValue, SomeGuard) 372 : detail::MaybeStorage<T>{std::forward<U>(aValue)} {} 373 374 using detail::MaybeStorage<T>::mIsSome; 375 using detail::MaybeStorage<T>::mStorage; 376 377 void poisonData() { detail::MaybePoisoner<T>::poison(&mStorage.val); } 378 379 public: 380 using ValueType = T; 381 382 MOZ_ALLOW_TEMPORARY constexpr Maybe() = default; 383 384 MOZ_ALLOW_TEMPORARY MOZ_IMPLICIT constexpr Maybe(Nothing) : Maybe{} {} 385 386 template <typename... Args> 387 constexpr explicit Maybe(std::in_place_t, Args&&... aArgs) 388 : detail::MaybeStorage<T>{std::in_place, std::forward<Args>(aArgs)...} {} 389 390 /** 391 * Maybe<T> can be copy-constructed from a Maybe<U> if T is constructible from 392 * a const U&. 393 */ 394 template <typename U, 395 std::enable_if_t<std::is_constructible_v<T, const U&>, bool> = true> 396 MOZ_IMPLICIT Maybe(const Maybe<U>& aOther) { 397 if (aOther.isSome()) { 398 emplace(*aOther); 399 } 400 } 401 402 template <typename U, std::enable_if_t<!std::is_constructible_v<T, const U&>, 403 bool> = true> 404 explicit Maybe(const Maybe<U>& aOther) = delete; 405 406 /** 407 * Maybe<T> can be move-constructed from a Maybe<U> if T is constructible from 408 * a U&&. 409 */ 410 template <typename U, 411 std::enable_if_t<std::is_constructible_v<T, U&&>, bool> = true> 412 MOZ_IMPLICIT Maybe(Maybe<U>&& aOther) { 413 if (aOther.isSome()) { 414 emplace(std::move(*aOther)); 415 aOther.reset(); 416 } 417 } 418 template <typename U, 419 std::enable_if_t<!std::is_constructible_v<T, U&&>, bool> = true> 420 explicit Maybe(Maybe<U>&& aOther) = delete; 421 422 template <typename U, 423 std::enable_if_t<std::is_constructible_v<T, const U&>, bool> = true> 424 Maybe& operator=(const Maybe<U>& aOther) { 425 if (aOther.isSome()) { 426 if (mIsSome) { 427 ref() = aOther.ref(); 428 } else { 429 emplace(*aOther); 430 } 431 } else { 432 reset(); 433 } 434 return *this; 435 } 436 437 template <typename U, std::enable_if_t<!std::is_constructible_v<T, const U&>, 438 bool> = true> 439 Maybe& operator=(const Maybe<U>& aOther) = delete; 440 441 template <typename U, 442 std::enable_if_t<std::is_constructible_v<T, U&&>, bool> = true> 443 Maybe& operator=(Maybe<U>&& aOther) { 444 if (aOther.isSome()) { 445 if (mIsSome) { 446 ref() = std::move(aOther.ref()); 447 } else { 448 emplace(std::move(*aOther)); 449 } 450 aOther.reset(); 451 } else { 452 reset(); 453 } 454 455 return *this; 456 } 457 458 template <typename U, 459 std::enable_if_t<!std::is_constructible_v<T, U&&>, bool> = true> 460 Maybe& operator=(Maybe<U>&& aOther) = delete; 461 462 constexpr Maybe& operator=(Nothing) { 463 reset(); 464 return *this; 465 } 466 467 /* Methods that check whether this Maybe contains a value */ 468 constexpr explicit operator bool() const { return isSome(); } 469 constexpr bool isSome() const { return mIsSome; } 470 constexpr bool isNothing() const { return !mIsSome; } 471 472 /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|. 473 */ 474 constexpr T value() const&; 475 constexpr T value() &&; 476 constexpr T value() const&&; 477 478 /** 479 * Move the contents of this Maybe<T> out of internal storage and return it 480 * without calling the destructor. The internal storage is also reset to 481 * avoid multiple calls. Unsafe unless |isSome()|. 482 */ 483 constexpr T extract() { 484 MOZ_RELEASE_ASSERT(isSome()); 485 T v = std::move(mStorage.val); 486 reset(); 487 return v; 488 } 489 490 /** 491 * Returns the value (possibly |Nothing()|) by moving it out of this Maybe<T> 492 * and leaving |Nothing()| in its place. 493 */ 494 Maybe<T> take() { return std::exchange(*this, Nothing()); } 495 496 /* 497 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns 498 * the default value provided. 499 * 500 * Note: If the value passed to aDefault is not the result of a trivial 501 * expression, but expensive to evaluate, e.g. |valueOr(ExpensiveFunction())|, 502 * use |valueOrFrom| instead, e.g. 503 * |valueOrFrom([arg] { return ExpensiveFunction(arg); })|. This ensures 504 * that the expensive expression is only evaluated when its result will 505 * actually be used. 506 */ 507 template <typename V> 508 constexpr T valueOr(V&& aDefault) const { 509 if (isSome()) { 510 return ref(); 511 } 512 return std::forward<V>(aDefault); 513 } 514 515 /* 516 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns 517 * the value returned from the function or functor provided. 518 */ 519 template <typename F> 520 constexpr T valueOrFrom(F&& aFunc) const { 521 if (isSome()) { 522 return ref(); 523 } 524 return aFunc(); 525 } 526 527 /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|. 528 */ 529 T* ptr(); 530 constexpr const T* ptr() const; 531 532 /* 533 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|, 534 * returns the default value provided. 535 */ 536 T* ptrOr(T* aDefault) { 537 if (isSome()) { 538 return ptr(); 539 } 540 return aDefault; 541 } 542 543 constexpr const T* ptrOr(const T* aDefault) const { 544 if (isSome()) { 545 return ptr(); 546 } 547 return aDefault; 548 } 549 550 /* 551 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|, 552 * returns the value returned from the function or functor provided. 553 */ 554 template <typename F> 555 T* ptrOrFrom(F&& aFunc) { 556 if (isSome()) { 557 return ptr(); 558 } 559 return aFunc(); 560 } 561 562 template <typename F> 563 const T* ptrOrFrom(F&& aFunc) const { 564 if (isSome()) { 565 return ptr(); 566 } 567 return aFunc(); 568 } 569 570 constexpr T* operator->(); 571 constexpr const T* operator->() const; 572 573 /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */ 574 constexpr T& ref() & MOZ_LIFETIME_BOUND; 575 constexpr const T& ref() const& MOZ_LIFETIME_BOUND; 576 constexpr T&& ref() && MOZ_LIFETIME_BOUND; 577 constexpr const T&& ref() const&& MOZ_LIFETIME_BOUND; 578 579 /* 580 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns 581 * the default value provided. 582 */ 583 constexpr T& refOr(T& aDefault MOZ_LIFETIME_BOUND) MOZ_LIFETIME_BOUND { 584 if (isSome()) { 585 return ref(); 586 } 587 return aDefault; 588 } 589 590 constexpr const T& refOr(const T& aDefault MOZ_LIFETIME_BOUND) const 591 MOZ_LIFETIME_BOUND { 592 if (isSome()) { 593 return ref(); 594 } 595 return aDefault; 596 } 597 598 /* 599 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the 600 * value returned from the function or functor provided. 601 */ 602 template <typename F> 603 constexpr T& refOrFrom(F&& aFunc) { 604 if (isSome()) { 605 return ref(); 606 } 607 return aFunc(); 608 } 609 610 template <typename F> 611 constexpr const T& refOrFrom(F&& aFunc) const { 612 if (isSome()) { 613 return ref(); 614 } 615 return aFunc(); 616 } 617 618 constexpr T& operator*() & MOZ_LIFETIME_BOUND; 619 constexpr const T& operator*() const& MOZ_LIFETIME_BOUND; 620 constexpr T&& operator*() && MOZ_LIFETIME_BOUND; 621 constexpr const T&& operator*() const&& MOZ_LIFETIME_BOUND; 622 623 /* If |isSome()|, runs the provided function or functor on the contents of 624 * this Maybe. */ 625 template <typename Func> 626 constexpr Maybe& apply(Func&& aFunc) & { 627 if (isSome()) { 628 std::forward<Func>(aFunc)(ref()); 629 } 630 return *this; 631 } 632 633 template <typename Func> 634 constexpr const Maybe& apply(Func&& aFunc) const& { 635 if (isSome()) { 636 std::forward<Func>(aFunc)(ref()); 637 } 638 return *this; 639 } 640 641 template <typename Func> 642 constexpr Maybe& apply(Func&& aFunc) && { 643 if (isSome()) { 644 std::forward<Func>(aFunc)(extract()); 645 } 646 return *this; 647 } 648 649 template <typename Func> 650 constexpr Maybe& apply(Func&& aFunc) const&& { 651 if (isSome()) { 652 std::forward<Func>(aFunc)(extract()); 653 } 654 return *this; 655 } 656 657 /* 658 * If |isSome()|, runs the provided function and returns the result wrapped 659 * in a Maybe. If |isNothing()|, returns an empty Maybe value with the same 660 * value type as what the provided function would have returned. 661 */ 662 template <typename Func> 663 constexpr auto map(Func&& aFunc) & { 664 if (isSome()) { 665 return Some(std::forward<Func>(aFunc)(ref())); 666 } 667 return Maybe<decltype(std::forward<Func>(aFunc)(ref()))>{}; 668 } 669 670 template <typename Func> 671 constexpr auto map(Func&& aFunc) const& { 672 if (isSome()) { 673 return Some(std::forward<Func>(aFunc)(ref())); 674 } 675 return Maybe<decltype(std::forward<Func>(aFunc)(ref()))>{}; 676 } 677 678 template <typename Func> 679 constexpr auto map(Func&& aFunc) && { 680 if (isSome()) { 681 return Some(std::forward<Func>(aFunc)(extract())); 682 } 683 return Maybe<decltype(std::forward<Func>(aFunc)(extract()))>{}; 684 } 685 686 template <typename Func> 687 constexpr auto map(Func&& aFunc) const&& { 688 if (isSome()) { 689 return Some(std::forward<Func>(aFunc)(extract())); 690 } 691 return Maybe<decltype(std::forward<Func>(aFunc)(extract()))>{}; 692 } 693 694 /* 695 * If |isSome()|, runs the provided function or functor on the contents of 696 * this Maybe and returns the result. Note that the provided function or 697 * functor must return a Maybe<U> of any type U. 698 * If |isNothing()|, returns an empty Maybe value with the same type as what 699 * the provided function would have returned. 700 */ 701 template <typename Func> 702 constexpr auto andThen(Func&& aFunc) & { 703 static_assert(std::is_invocable_v<Func, T&>); 704 using U = std::invoke_result_t<Func, T&>; 705 static_assert(detail::IsMaybe<U>::value); 706 if (isSome()) { 707 return std::invoke(std::forward<Func>(aFunc), ref()); 708 } 709 return std::remove_cv_t<std::remove_reference_t<U>>{}; 710 } 711 712 template <typename Func> 713 constexpr auto andThen(Func&& aFunc) const& { 714 static_assert(std::is_invocable_v<Func, const T&>); 715 using U = std::invoke_result_t<Func, const T&>; 716 static_assert(detail::IsMaybe<U>::value); 717 if (isSome()) { 718 return std::invoke(std::forward<Func>(aFunc), ref()); 719 } 720 return std::remove_cv_t<std::remove_reference_t<U>>{}; 721 } 722 723 template <typename Func> 724 constexpr auto andThen(Func&& aFunc) && { 725 static_assert(std::is_invocable_v<Func, T&&>); 726 using U = std::invoke_result_t<Func, T&&>; 727 static_assert(detail::IsMaybe<U>::value); 728 if (isSome()) { 729 return std::invoke(std::forward<Func>(aFunc), extract()); 730 } 731 return std::remove_cv_t<std::remove_reference_t<U>>{}; 732 } 733 734 template <typename Func> 735 constexpr auto andThen(Func&& aFunc) const&& { 736 static_assert(std::is_invocable_v<Func, const T&&>); 737 using U = std::invoke_result_t<Func, const T&&>; 738 static_assert(detail::IsMaybe<U>::value); 739 if (isSome()) { 740 return std::invoke(std::forward<Func>(aFunc), extract()); 741 } 742 return std::remove_cv_t<std::remove_reference_t<U>>{}; 743 } 744 745 /* 746 * If |isNothing()|, runs the provided function or functor and returns its 747 * result. If |isSome()|, returns the contained value wrapped in a Maybe. 748 */ 749 template <typename Func> 750 constexpr Maybe orElse(Func&& aFunc) & { 751 static_assert(std::is_invocable_v<Func>); 752 using U = std::invoke_result_t<Func>; 753 static_assert( 754 std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); 755 if (isSome()) { 756 return *this; 757 } 758 return std::invoke(std::forward<Func>(aFunc)); 759 } 760 761 template <typename Func> 762 constexpr Maybe orElse(Func&& aFunc) const& { 763 static_assert(std::is_invocable_v<Func>); 764 using U = std::invoke_result_t<Func>; 765 static_assert( 766 std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); 767 if (isSome()) { 768 return *this; 769 } 770 return std::invoke(std::forward<Func>(aFunc)); 771 } 772 773 template <typename Func> 774 constexpr Maybe orElse(Func&& aFunc) && { 775 static_assert(std::is_invocable_v<Func>); 776 using U = std::invoke_result_t<Func>; 777 static_assert( 778 std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); 779 if (isSome()) { 780 return std::move(*this); 781 } 782 return std::invoke(std::forward<Func>(aFunc)); 783 } 784 785 template <typename Func> 786 constexpr Maybe orElse(Func&& aFunc) const&& { 787 static_assert(std::is_invocable_v<Func>); 788 using U = std::invoke_result_t<Func>; 789 static_assert( 790 std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); 791 if (isSome()) { 792 return std::move(*this); 793 } 794 return std::invoke(std::forward<Func>(aFunc)); 795 } 796 797 /* begin() and end() implementation */ 798 private: 799 template <typename U> 800 struct Iterator { 801 using iterator_type = Iterator<U>; 802 using value_type = U; 803 using difference_type = std::ptrdiff_t; 804 using reference = value_type&; 805 using pointer = value_type*; 806 using iterator_category = std::forward_iterator_tag; 807 808 constexpr Iterator() = default; 809 constexpr explicit Iterator(pointer aValue) : mValue(aValue) {} 810 811 constexpr reference operator*() const { return *mValue; }; 812 813 constexpr pointer operator->() const { return mValue; } 814 815 constexpr iterator_type& operator++() { 816 mValue = nullptr; 817 return *this; 818 } 819 820 constexpr iterator_type operator++(int) { 821 iterator_type it{mValue}; 822 mValue = nullptr; 823 return it; 824 } 825 826 constexpr auto operator<=>(const Iterator&) const = default; 827 828 private: 829 pointer mValue; 830 }; 831 832 public: 833 using iterator = Iterator<T>; 834 using const_iterator = Iterator<const T>; 835 836 constexpr iterator begin() { return iterator{ptrOr(nullptr)}; } 837 constexpr const_iterator begin() const { 838 return const_iterator{ptrOr(nullptr)}; 839 } 840 constexpr const_iterator cbegin() const { return begin(); } 841 842 constexpr iterator end() { return iterator{nullptr}; } 843 constexpr const_iterator end() const { return const_iterator{nullptr}; } 844 constexpr const_iterator cend() const { return end(); } 845 846 /* If |isSome()|, empties this Maybe and destroys its contents. */ 847 constexpr void reset() { 848 if (isSome()) { 849 if constexpr (!std::is_trivially_destructible_v<T>) { 850 /* 851 * Static analyzer gets confused if we have Maybe<MutexAutoLock>, 852 * so we suppress thread-safety warnings here 853 */ 854 MOZ_PUSH_IGNORE_THREAD_SAFETY 855 ref().T::~T(); 856 MOZ_POP_THREAD_SAFETY 857 poisonData(); 858 } 859 mIsSome = false; 860 } 861 } 862 863 /* 864 * Constructs a T value in-place in this empty Maybe<T>'s storage. The 865 * arguments to |emplace()| are the parameters to T's constructor. 866 */ 867 template <typename... Args> 868 constexpr void emplace(Args&&... aArgs); 869 870 template <typename U> 871 constexpr std::enable_if_t<std::is_same_v<T, U> && 872 std::is_copy_constructible_v<U> && 873 !std::is_move_constructible_v<U>> 874 emplace(U&& aArgs) { 875 emplace(aArgs); 876 } 877 878 friend std::ostream& operator<<(std::ostream& aStream, 879 const Maybe<T>& aMaybe) { 880 if (aMaybe) { 881 aStream << aMaybe.ref(); 882 } else { 883 aStream << "<Nothing>"; 884 } 885 return aStream; 886 } 887 }; 888 889 template <typename T> 890 class Maybe<T&> { 891 public: 892 constexpr Maybe() = default; 893 constexpr MOZ_IMPLICIT Maybe(Nothing) {} 894 895 void emplace(T& aRef) { mValue = &aRef; } 896 897 /* Methods that check whether this Maybe contains a value */ 898 constexpr explicit operator bool() const { return isSome(); } 899 constexpr bool isSome() const { return mValue; } 900 constexpr bool isNothing() const { return !mValue; } 901 902 T& ref() const { 903 MOZ_RELEASE_ASSERT(isSome()); 904 return *mValue; 905 } 906 907 T* operator->() const { return &ref(); } 908 T& operator*() const { return ref(); } 909 910 // Deliberately not defining value and ptr accessors, as these may be 911 // confusing on a reference-typed Maybe. 912 913 // XXX Should we define refOr? 914 915 void reset() { mValue = nullptr; } 916 917 template <typename Func> 918 const Maybe& apply(Func&& aFunc) const { 919 if (isSome()) { 920 std::forward<Func>(aFunc)(ref()); 921 } 922 return *this; 923 } 924 925 template <typename Func> 926 auto map(Func&& aFunc) const { 927 Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val; 928 if (isSome()) { 929 val.emplace(std::forward<Func>(aFunc)(ref())); 930 } 931 return val; 932 } 933 934 template <typename Func> 935 constexpr auto andThen(Func&& aFunc) const { 936 static_assert(std::is_invocable_v<Func, T&>); 937 using U = std::invoke_result_t<Func, T&>; 938 static_assert(detail::IsMaybe<U>::value); 939 if (isSome()) { 940 return std::invoke(std::forward<Func>(aFunc), ref()); 941 } 942 return std::remove_cv_t<std::remove_reference_t<U>>{}; 943 } 944 945 template <typename Func> 946 constexpr Maybe orElse(Func&& aFunc) const { 947 static_assert(std::is_invocable_v<Func>); 948 using U = std::invoke_result_t<Func>; 949 static_assert( 950 std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); 951 if (isSome()) { 952 return *this; 953 } 954 return std::invoke(std::forward<Func>(aFunc)); 955 } 956 957 bool refEquals(const Maybe<T&>& aOther) const { 958 return mValue == aOther.mValue; 959 } 960 961 bool refEquals(const T& aOther) const { return mValue == &aOther; } 962 963 private: 964 T* mValue = nullptr; 965 }; 966 967 template <typename T> 968 constexpr T Maybe<T>::value() const& { 969 MOZ_RELEASE_ASSERT(isSome()); 970 return ref(); 971 } 972 973 template <typename T> 974 constexpr T Maybe<T>::value() && { 975 MOZ_RELEASE_ASSERT(isSome()); 976 return std::move(ref()); 977 } 978 979 template <typename T> 980 constexpr T Maybe<T>::value() const&& { 981 MOZ_RELEASE_ASSERT(isSome()); 982 return std::move(ref()); 983 } 984 985 template <typename T> 986 T* Maybe<T>::ptr() { 987 MOZ_RELEASE_ASSERT(isSome()); 988 return &ref(); 989 } 990 991 template <typename T> 992 constexpr const T* Maybe<T>::ptr() const { 993 MOZ_RELEASE_ASSERT(isSome()); 994 return &ref(); 995 } 996 997 template <typename T> 998 constexpr T* Maybe<T>::operator->() { 999 MOZ_RELEASE_ASSERT(isSome()); 1000 return ptr(); 1001 } 1002 1003 template <typename T> 1004 constexpr const T* Maybe<T>::operator->() const { 1005 MOZ_RELEASE_ASSERT(isSome()); 1006 return ptr(); 1007 } 1008 1009 template <typename T> 1010 constexpr T& Maybe<T>::ref() & { 1011 MOZ_RELEASE_ASSERT(isSome()); 1012 return mStorage.val; 1013 } 1014 1015 template <typename T> 1016 constexpr const T& Maybe<T>::ref() const& { 1017 MOZ_RELEASE_ASSERT(isSome()); 1018 return mStorage.val; 1019 } 1020 1021 template <typename T> 1022 constexpr T&& Maybe<T>::ref() && { 1023 MOZ_RELEASE_ASSERT(isSome()); 1024 return std::move(mStorage.val); 1025 } 1026 1027 template <typename T> 1028 constexpr const T&& Maybe<T>::ref() const&& { 1029 MOZ_RELEASE_ASSERT(isSome()); 1030 return std::move(mStorage.val); 1031 } 1032 1033 template <typename T> 1034 constexpr T& Maybe<T>::operator*() & { 1035 MOZ_RELEASE_ASSERT(isSome()); 1036 return ref(); 1037 } 1038 1039 template <typename T> 1040 constexpr const T& Maybe<T>::operator*() const& { 1041 MOZ_RELEASE_ASSERT(isSome()); 1042 return ref(); 1043 } 1044 1045 template <typename T> 1046 constexpr T&& Maybe<T>::operator*() && { 1047 MOZ_RELEASE_ASSERT(isSome()); 1048 return std::move(ref()); 1049 } 1050 1051 template <typename T> 1052 constexpr const T&& Maybe<T>::operator*() const&& { 1053 MOZ_RELEASE_ASSERT(isSome()); 1054 return std::move(ref()); 1055 } 1056 1057 template <typename T> 1058 template <typename... Args> 1059 constexpr void Maybe<T>::emplace(Args&&... aArgs) { 1060 MOZ_RELEASE_ASSERT(!isSome()); 1061 ::new (KnownNotNull, &mStorage.val) T(std::forward<Args>(aArgs)...); 1062 mIsSome = true; 1063 } 1064 1065 /* 1066 * Some() creates a Maybe<T> value containing the provided T value. If T has a 1067 * move constructor, it's used to make this as efficient as possible. 1068 * 1069 * Some() selects the type of Maybe it returns by removing any const, volatile, 1070 * or reference qualifiers from the type of the value you pass to it. This gives 1071 * it more intuitive behavior when used in expressions, but it also means that 1072 * if you need to construct a Maybe value that holds a const, volatile, or 1073 * reference value, you need to use emplace() instead. 1074 */ 1075 template <typename T, typename U> 1076 constexpr Maybe<U> Some(T&& aValue) { 1077 return {std::forward<T>(aValue), typename Maybe<U>::SomeGuard{}}; 1078 } 1079 1080 template <typename T> 1081 constexpr Maybe<T&> SomeRef(T& aValue) { 1082 Maybe<T&> value; 1083 value.emplace(aValue); 1084 return value; 1085 } 1086 1087 template <typename T> 1088 constexpr Maybe<T&> ToMaybeRef(T* const aPtr) { 1089 return aPtr ? SomeRef(*aPtr) : Nothing{}; 1090 } 1091 1092 template <typename T> 1093 Maybe<std::remove_cv_t<std::remove_reference_t<T>>> ToMaybe(T* aPtr) { 1094 if (aPtr) { 1095 return Some(*aPtr); 1096 } 1097 return Nothing(); 1098 } 1099 1100 /* 1101 * Two Maybe<T> values are equal if 1102 * - both are Nothing, or 1103 * - both are Some, and the values they contain are equal. 1104 */ 1105 template <typename T> 1106 constexpr bool operator==(const Maybe<T>& aLHS, const Maybe<T>& aRHS) { 1107 static_assert(!std::is_reference_v<T>, 1108 "operator== is not defined for Maybe<T&>, compare values or " 1109 "addresses explicitly instead"); 1110 if (aLHS.isNothing() != aRHS.isNothing()) { 1111 return false; 1112 } 1113 return aLHS.isNothing() || *aLHS == *aRHS; 1114 } 1115 1116 template <typename T> 1117 constexpr bool operator!=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) { 1118 return !(aLHS == aRHS); 1119 } 1120 1121 /* 1122 * We support comparison to Nothing to allow reasonable expressions like: 1123 * if (maybeValue == Nothing()) { ... } 1124 */ 1125 template <typename T> 1126 constexpr bool operator==(const Maybe<T>& aLHS, const Nothing& aRHS) { 1127 return aLHS.isNothing(); 1128 } 1129 1130 template <typename T> 1131 constexpr bool operator!=(const Maybe<T>& aLHS, const Nothing& aRHS) { 1132 return !(aLHS == aRHS); 1133 } 1134 1135 template <typename T> 1136 constexpr bool operator==(const Nothing& aLHS, const Maybe<T>& aRHS) { 1137 return aRHS.isNothing(); 1138 } 1139 1140 template <typename T> 1141 constexpr bool operator!=(const Nothing& aLHS, const Maybe<T>& aRHS) { 1142 return !(aLHS == aRHS); 1143 } 1144 1145 /* 1146 * Maybe<T> values are ordered in the same way T values are ordered, except that 1147 * Nothing comes before anything else. 1148 */ 1149 template <typename T> 1150 constexpr bool operator<(const Maybe<T>& aLHS, const Maybe<T>& aRHS) { 1151 if (aLHS.isNothing()) { 1152 return aRHS.isSome(); 1153 } 1154 if (aRHS.isNothing()) { 1155 return false; 1156 } 1157 return *aLHS < *aRHS; 1158 } 1159 1160 template <typename T> 1161 constexpr bool operator>(const Maybe<T>& aLHS, const Maybe<T>& aRHS) { 1162 return !(aLHS < aRHS || aLHS == aRHS); 1163 } 1164 1165 template <typename T> 1166 constexpr bool operator<=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) { 1167 return aLHS < aRHS || aLHS == aRHS; 1168 } 1169 1170 template <typename T> 1171 constexpr bool operator>=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) { 1172 return !(aLHS < aRHS); 1173 } 1174 1175 template <typename T> 1176 inline void ImplCycleCollectionTraverse( 1177 nsCycleCollectionTraversalCallback& aCallback, mozilla::Maybe<T>& aField, 1178 const char* aName, uint32_t aFlags = 0) { 1179 if (aField) { 1180 ImplCycleCollectionTraverse(aCallback, aField.ref(), aName, aFlags); 1181 } 1182 } 1183 1184 template <typename T> 1185 inline void ImplCycleCollectionUnlink(mozilla::Maybe<T>& aField) { 1186 if (aField) { 1187 ImplCycleCollectionUnlink(aField.ref()); 1188 } 1189 } 1190 1191 } // namespace mozilla 1192 1193 #endif /* mozilla_Maybe_h */