/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* A class for optional values and in-place lazy construction. */
#ifndef mozilla_Maybe_h #define mozilla_Maybe_h
#include <functional> #include <new> // for placement new #include <ostream> #include <type_traits> #include <utility>
// You would think that poisoning Maybe instances could just be a call // to mozWritePoison. Unfortunately, using a simple call to // mozWritePoison generates poor code on MSVC for small structures. The // generated code contains (always not-taken) branches and does a bunch // of setup for `rep stos{l,q}`, even though we know at compile time // exactly how many words we're poisoning. Instead, we're going to // force MSVC to generate the code we want via recursive templates.
// Write the given poisonValue into p at offset*sizeof(uintptr_t). template <size_t offset> inlinevoid WritePoisonAtOffset(void* p, const uintptr_t poisonValue) {
memcpy(static_cast<char*>(p) + offset * sizeof(poisonValue), &poisonValue, sizeof(poisonValue));
}
// We can't generate inline code for large structures, though, because we'll // blow out recursive template instantiation limits, and the code would be // bloated to boot. So provide a fallback to the out-of-line poisoner. template <size_t ObjectSize> struct OutOfLinePoisoner { static MOZ_NEVER_INLINE void poison(void* p, const uintptr_t) {
mozWritePoison(p, ObjectSize);
}
};
/* * Maybe is a container class which contains either zero or one elements. It * serves two roles. It can represent values which are *semantically* optional, * augmenting a type with an explicit 'Nothing' value. In this role, it provides * methods that make it easy to work with values that may be missing, along with * equality and comparison operators so that Maybe values can be stored in * containers. Maybe values can be constructed conveniently in expressions using * type inference, as follows: * * void doSomething(Maybe<Foo> aFoo) { * if (aFoo) // Make sure that aFoo contains a value... * aFoo->takeAction(); // and then use |aFoo->| to access it. * } // |*aFoo| also works! * * doSomething(Nothing()); // Passes a Maybe<Foo> containing no value. * doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|. * * You'll note that it's important to check whether a Maybe contains a value * before using it, using conversion to bool, |isSome()|, or |isNothing()|. You * can avoid these checks, and sometimes write more readable code, using * |valueOr()|, |ptrOr()|, and |refOr()|, which allow you to retrieve the value * in the Maybe and provide a default for the 'Nothing' case. You can also use * |apply()| to call a function only if the Maybe holds a value, and |map()| to * transform the value in the Maybe, returning another Maybe with a possibly * different type. * * Maybe's other role is to support lazily constructing objects without using * dynamic storage. A Maybe directly contains storage for a value, but it's * empty by default. |emplace()|, as mentioned above, can be used to construct a * value in Maybe's storage. The value a Maybe contains can be destroyed by * calling |reset()|; this will happen automatically if a Maybe is destroyed * while holding a value. * * It's a common idiom in C++ to use a pointer as a 'Maybe' type, with a null * value meaning 'Nothing' and any other value meaning 'Some'. You can convert * from such a pointer to a Maybe value using 'ToMaybe()'. * * Maybe is inspired by similar types in the standard library of many other * languages (e.g. Haskell's Maybe and Rust's Option). In the C++ world it's * very similar to std::optional, which was proposed for C++14 and originated in * Boost. The most important differences between Maybe and std::optional are: * * - std::optional<T> may be compared with T. We deliberately forbid that. * - std::optional has |valueOr()|, equivalent to Maybe's |valueOr()|, but * lacks corresponding methods for |refOr()| and |ptrOr()|. * - std::optional lacks |map()| and |apply()|, making it less suitable for * functional-style code. * - std::optional lacks many convenience functions that Maybe has. Most * unfortunately, it lacks equivalents of the type-inferred constructor * functions |Some()| and |Nothing()|.
*/ template <class T> class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe
: private detail::MaybeStorage<T>, public detail::Maybe_CopyMove_Enabler<T> { template <typename, bool, bool, bool> friendclass detail::Maybe_CopyMove_Enabler;
template <typename U, typename V> friend constexpr Maybe<V> Some(U&& aValue);
/** * Maybe<T> can be copy-constructed from a Maybe<U> if T is constructible from * a const U&.
*/ template <typename U,
std::enable_if_t<std::is_constructible_v<T, const U&>, bool> = true>
MOZ_IMPLICIT Maybe(const Maybe<U>& aOther) { if (aOther.isSome()) {
emplace(*aOther);
}
}
/** * Maybe<T> can be move-constructed from a Maybe<U> if T is constructible from * a U&&.
*/ template <typename U,
std::enable_if_t<std::is_constructible_v<T, U&&>, bool> = true>
MOZ_IMPLICIT Maybe(Maybe<U>&& aOther) { if (aOther.isSome()) {
emplace(std::move(*aOther));
aOther.reset();
}
} template <typename U,
std::enable_if_t<!std::is_constructible_v<T, U&&>, bool> = true> explicit Maybe(Maybe<U>&& aOther) = delete;
/* Methods that check whether this Maybe contains a value */
constexpr explicitoperatorbool() const { return isSome(); }
constexpr bool isSome() const { return mIsSome; }
constexpr bool isNothing() const { return !mIsSome; }
/* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|.
*/
constexpr T value() const&;
constexpr T value() &&;
constexpr T value() const&&;
/** * Move the contents of this Maybe<T> out of internal storage and return it * without calling the destructor. The internal storage is also reset to * avoid multiple calls. Unsafe unless |isSome()|.
*/
constexpr T extract() {
MOZ_RELEASE_ASSERT(isSome());
T v = std::move(mStorage.val);
reset(); return v;
}
/** * Returns the value (possibly |Nothing()|) by moving it out of this Maybe<T> * and leaving |Nothing()| in its place.
*/
Maybe<T> take() { return std::exchange(*this, Nothing()); }
/* * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns * the default value provided. * * Note: If the value passed to aDefault is not the result of a trivial * expression, but expensive to evaluate, e.g. |valueOr(ExpensiveFunction())|, * use |valueOrFrom| instead, e.g. * |valueOrFrom([arg] { return ExpensiveFunction(arg); })|. This ensures * that the expensive expression is only evaluated when its result will * actually be used.
*/ template <typename V>
constexpr T valueOr(V&& aDefault) const { if (isSome()) { return ref();
} return std::forward<V>(aDefault);
}
/* * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns * the value returned from the function or functor provided.
*/ template <typename F>
constexpr T valueOrFrom(F&& aFunc) const { if (isSome()) { return ref();
} return aFunc();
}
/* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|.
*/
T* ptr();
constexpr const T* ptr() const;
/* * Returns the contents of this Maybe<T> by pointer. If |isNothing()|, * returns the default value provided.
*/
T* ptrOr(T* aDefault) { if (isSome()) { return ptr();
} return aDefault;
}
/* * Returns the contents of this Maybe<T> by pointer. If |isNothing()|, * returns the value returned from the function or functor provided.
*/ template <typename F>
T* ptrOrFrom(F&& aFunc) { if (isSome()) { return ptr();
} return aFunc();
}
/* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
constexpr T& ref() &;
constexpr const T& ref() const&;
constexpr T&& ref() &&;
constexpr const T&& ref() const&&;
/* * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns * the default value provided.
*/
constexpr T& refOr(T& aDefault) { if (isSome()) { return ref();
} return aDefault;
}
/* * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the * value returned from the function or functor provided.
*/ template <typename F>
constexpr T& refOrFrom(F&& aFunc) { if (isSome()) { return ref();
} return aFunc();
}
/* If |isSome()|, runs the provided function or functor on the contents of
* this Maybe. */ template <typename Func>
constexpr Maybe& apply(Func&& aFunc) & { if (isSome()) {
std::forward<Func>(aFunc)(ref());
} return *this;
}
/* * If |isSome()|, runs the provided function and returns the result wrapped * in a Maybe. If |isNothing()|, returns an empty Maybe value with the same * value type as what the provided function would have returned.
*/ template <typename Func>
constexpr auto map(Func&& aFunc) & { if (isSome()) { return Some(std::forward<Func>(aFunc)(ref()));
} return Maybe<decltype(std::forward<Func>(aFunc)(ref()))>{};
}
template <typename Func>
constexpr auto map(Func&& aFunc) const& { if (isSome()) { return Some(std::forward<Func>(aFunc)(ref()));
} return Maybe<decltype(std::forward<Func>(aFunc)(ref()))>{};
}
template <typename Func>
constexpr auto map(Func&& aFunc) && { if (isSome()) { return Some(std::forward<Func>(aFunc)(extract()));
} return Maybe<decltype(std::forward<Func>(aFunc)(extract()))>{};
}
template <typename Func>
constexpr auto map(Func&& aFunc) const&& { if (isSome()) { return Some(std::forward<Func>(aFunc)(extract()));
} return Maybe<decltype(std::forward<Func>(aFunc)(extract()))>{};
}
/* * If |isSome()|, runs the provided function or functor on the contents of * this Maybe and returns the result. Note that the provided function or * functor must return a Maybe<U> of any type U. * If |isNothing()|, returns an empty Maybe value with the same type as what * the provided function would have returned.
*/ template <typename Func>
constexpr auto andThen(Func&& aFunc) & {
static_assert(std::is_invocable_v<Func, T&>); using U = std::invoke_result_t<Func, T&>;
static_assert(detail::IsMaybe<U>::value); if (isSome()) { return std::invoke(std::forward<Func>(aFunc), ref());
} return std::remove_cv_t<std::remove_reference_t<U>>{};
}
template <typename Func>
constexpr auto andThen(Func&& aFunc) const& {
static_assert(std::is_invocable_v<Func, const T&>); using U = std::invoke_result_t<Func, const T&>;
static_assert(detail::IsMaybe<U>::value); if (isSome()) { return std::invoke(std::forward<Func>(aFunc), ref());
} return std::remove_cv_t<std::remove_reference_t<U>>{};
}
template <typename Func>
constexpr auto andThen(Func&& aFunc) && {
static_assert(std::is_invocable_v<Func, T&&>); using U = std::invoke_result_t<Func, T&&>;
static_assert(detail::IsMaybe<U>::value); if (isSome()) { return std::invoke(std::forward<Func>(aFunc), extract());
} return std::remove_cv_t<std::remove_reference_t<U>>{};
}
template <typename Func>
constexpr auto andThen(Func&& aFunc) const&& {
static_assert(std::is_invocable_v<Func, const T&&>); using U = std::invoke_result_t<Func, const T&&>;
static_assert(detail::IsMaybe<U>::value); if (isSome()) { return std::invoke(std::forward<Func>(aFunc), extract());
} return std::remove_cv_t<std::remove_reference_t<U>>{};
}
/* * If |isNothing()|, runs the provided function or functor and returns its * result. If |isSome()|, returns the contained value wrapped in a Maybe.
*/ template <typename Func>
constexpr Maybe orElse(Func&& aFunc) & {
static_assert(std::is_invocable_v<Func>); using U = std::invoke_result_t<Func>;
static_assert(
std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); if (isSome()) { return *this;
} return std::invoke(std::forward<Func>(aFunc));
}
template <typename Func>
constexpr Maybe orElse(Func&& aFunc) const& {
static_assert(std::is_invocable_v<Func>); using U = std::invoke_result_t<Func>;
static_assert(
std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); if (isSome()) { return *this;
} return std::invoke(std::forward<Func>(aFunc));
}
template <typename Func>
constexpr Maybe orElse(Func&& aFunc) && {
static_assert(std::is_invocable_v<Func>); using U = std::invoke_result_t<Func>;
static_assert(
std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); if (isSome()) { return std::move(*this);
} return std::invoke(std::forward<Func>(aFunc));
}
template <typename Func>
constexpr Maybe orElse(Func&& aFunc) const&& {
static_assert(std::is_invocable_v<Func>); using U = std::invoke_result_t<Func>;
static_assert(
std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); if (isSome()) { return std::move(*this);
} return std::invoke(std::forward<Func>(aFunc));
}
/* If |isSome()|, empties this Maybe and destroys its contents. */
constexpr void reset() { if (isSome()) { if constexpr (!std::is_trivially_destructible_v<T>) { /* * Static analyzer gets confused if we have Maybe<MutexAutoLock>, * so we suppress thread-safety warnings here
*/
MOZ_PUSH_IGNORE_THREAD_SAFETY
ref().T::~T();
MOZ_POP_THREAD_SAFETY
poisonData();
}
mIsSome = false;
}
}
/* * Constructs a T value in-place in this empty Maybe<T>'s storage. The * arguments to |emplace()| are the parameters to T's constructor.
*/ template <typename... Args>
constexpr void emplace(Args&&... aArgs);
/* * Some() creates a Maybe<T> value containing the provided T value. If T has a * move constructor, it's used to make this as efficient as possible. * * Some() selects the type of Maybe it returns by removing any const, volatile, * or reference qualifiers from the type of the value you pass to it. This gives * it more intuitive behavior when used in expressions, but it also means that * if you need to construct a Maybe value that holds a const, volatile, or * reference value, you need to use emplace() instead.
*/ template <typename T, typename U>
constexpr Maybe<U> Some(T&& aValue) { return {std::forward<T>(aValue), typename Maybe<U>::SomeGuard{}};
}
/* * Two Maybe<T> values are equal if * - both are Nothing, or * - both are Some, and the values they contain are equal.
*/ template <typename T>
constexpr booloperator==(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
static_assert(!std::is_reference_v<T>, "operator== is not defined for Maybe, compare values or " "addresses explicitly instead"); if (aLHS.isNothing() != aRHS.isNothing()) { returnfalse;
} return aLHS.isNothing() || *aLHS == *aRHS;
}
/* * Maybe<T> values are ordered in the same way T values are ordered, except that * Nothing comes before anything else.
*/ template <typename T>
constexpr booloperator<(const Maybe<T>& aLHS, const Maybe<T>& aRHS) { if (aLHS.isNothing()) { return aRHS.isSome();
} if (aRHS.isNothing()) { returnfalse;
} return *aLHS < *aRHS;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.