diff --git a/mfbt/Maybe.h b/mfbt/Maybe.h index bc123b047f..6c0fbe7ba2 100644 --- a/mfbt/Maybe.h +++ b/mfbt/Maybe.h @@ -83,7 +83,15 @@ template class Maybe { bool mIsSome; - AlignedStorage2 mStorage; + + // To support |Maybe| we give |mStorage| the type |T| with any + // const-ness removed. That allows us to |emplace()| an object into + // |mStorage|. Since we treat the contained object as having type |T| + // everywhere else (both internally, and when exposed via public methods) the + // contained object is still treated as const once stored since |const| is + // part of |T|'s type signature. + typedef typename RemoveCV::Type StorageType; + AlignedStorage2 mStorage; public: typedef T ValueType; @@ -453,6 +461,72 @@ public: } }; +template +class Maybe { + public: + constexpr Maybe() = default; + constexpr MOZ_IMPLICIT Maybe(Nothing) {} + + void emplace(T& aRef) { mValue = &aRef; } + + /* Methods that check whether this Maybe contains a value */ + explicit operator bool() const { return isSome(); } + bool isSome() const { return mValue; } + bool isNothing() const { return !mValue; } + + T& ref() const { + MOZ_DIAGNOSTIC_ASSERT(isSome()); + return *mValue; + } + + T* operator->() const { return &ref(); } + T& operator*() const { return ref(); } + + // Deliberately not defining value and ptr accessors, as these may be + // confusing on a reference-typed Maybe. + + // XXX Should we define refOr? + + void reset() { mValue = nullptr; } + + template + Maybe& apply(Func&& aFunc) { + if (isSome()) { + std::forward(aFunc)(ref()); + } + return *this; + } + + template + const Maybe& apply(Func&& aFunc) const { + if (isSome()) { + std::forward(aFunc)(ref()); + } + return *this; + } + + template + auto map(Func&& aFunc) { + Maybe(aFunc)(ref()))> val; + if (isSome()) { + val.emplace(std::forward(aFunc)(ref())); + } + return val; + } + + template + auto map(Func&& aFunc) const { + Maybe(aFunc)(ref()))> val; + if (isSome()) { + val.emplace(std::forward(aFunc)(ref())); + } + return val; + } + + private: + T* mValue = nullptr; +}; + /* * Some() creates a Maybe value containing the provided T value. If T has a * move constructor, it's used to make this as efficient as possible. @@ -474,6 +548,13 @@ Some(T&& aValue) return value; } +template +Maybe SomeRef(T& aValue) { + Maybe value; + value.emplace(aValue); + return value; +} + template Maybe::Type>::Type> ToMaybe(T* aPtr) @@ -492,6 +573,9 @@ ToMaybe(T* aPtr) template bool operator==(const Maybe& aLHS, const Maybe& aRHS) { + static_assert(!std::is_reference::value, + "operator== is not defined for Maybe, compare values or " + "addresses explicitly instead"); if (aLHS.isNothing() != aRHS.isNothing()) { return false; }