Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  Maybe.h   Sprache: C

 
/* -*- 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>

#include "mozilla/Alignment.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/MaybeStorageBase.h"
#include "mozilla/MemoryChecking.h"
#include "mozilla/OperatorNewExtensions.h"
#include "mozilla/Poison.h"
#include "mozilla/ThreadSafety.h"

class nsCycleCollectionTraversalCallback;

template <typename T>
inline void CycleCollectionNoteChild(
    nsCycleCollectionTraversalCallback& aCallback, T* aChild, const char* aName,
    uint32_t aFlags);

namespace mozilla {

struct Nothing {};

inline constexpr bool operator==(const Nothing&, const Nothing&) {
  return true;
}

template <class T>
class Maybe;

namespace detail {

// 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>
inline void WritePoisonAtOffset(void* p, const uintptr_t poisonValue) {
  memcpy(static_cast<char*>(p) + offset * sizeof(poisonValue), &poisonValue,
         sizeof(poisonValue));
}

template <size_t Offset, size_t NOffsets>
struct InlinePoisoner {
  static void poison(void* p, const uintptr_t poisonValue) {
    WritePoisonAtOffset<Offset>(p, poisonValue);
    InlinePoisoner<Offset + 1, NOffsets>::poison(p, poisonValue);
  }
};

template <size_t N>
struct InlinePoisoner<N, N> {
  static void poison(void*, const uintptr_t) {
    // All done!
  }
};

// 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);
  }
};

template <typename T>
inline void PoisonObject(T* p) {
  const uintptr_t POISON = mozPoisonValue();
  std::conditional_t<(sizeof(T) <= 8 * sizeof(POISON)),
                     InlinePoisoner<0, sizeof(T) / sizeof(POISON)>,
                     OutOfLinePoisoner<sizeof(T)>>::poison(p, POISON);
}

template <typename T>
struct MaybePoisoner {
  static const size_t N = sizeof(T);

  static void poison(void* aPtr) {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    if (N >= sizeof(uintptr_t)) {
      PoisonObject(static_cast<std::remove_cv_t<T>*>(aPtr));
    }
#endif
    MOZ_MAKE_MEM_UNDEFINED(aPtr, N);
  }
};

template <typename T,
          bool TriviallyDestructibleAndCopyable =
              IsTriviallyDestructibleAndCopyable<T>,
          bool Copyable = std::is_copy_constructible_v<T>,
          bool Movable = std::is_move_constructible_v<T>>
class Maybe_CopyMove_Enabler;

#define MOZ_MAYBE_COPY_OPS()                                                \
  Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler& aOther) {            \
    if (downcast(aOther).isSome()) {                                        \
      downcast(*this).emplace(*downcast(aOther));                           \
    }                                                                       \
  }                                                                         \
                                                                            \
  Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler& aOther) { \
    return downcast(*this).template operator= <T>(downcast(aOther));        \
  }

#define MOZ_MAYBE_MOVE_OPS()                                             \
  constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) {    \
    if (downcast(aOther).isSome()) {                                     \
      downcast(*this).emplace(std::move(*downcast(aOther)));             \
      downcast(aOther).reset();                                          \
    }                                                                    \
  }                                                                      \
                                                                         \
  constexpr Maybe_CopyMove_Enabler& operator=(                           \
      Maybe_CopyMove_Enabler&& aOther) {                                 \
    downcast(*this).template operator= <T>(std::move(downcast(aOther))); \
                                                                         \
    return *this;                                                        \
  }

#define MOZ_MAYBE_DOWNCAST()                                          \
  static constexpr Maybe<T>& downcast(Maybe_CopyMove_Enabler& aObj) { \
    return static_cast<Maybe<T>&>(aObj);                              \
  }                                                                   \
  static constexpr const Maybe<T>& downcast(                          \
      const Maybe_CopyMove_Enabler& aObj) {                           \
    return static_cast<const Maybe<T>&>(aObj);                        \
  }

template <typename T>
class Maybe_CopyMove_Enabler<T, truetruetrue> {
 public:
  Maybe_CopyMove_Enabler() = default;

  Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = default;
  Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = default;
  constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) {
    downcast(aOther).reset();
  }
  constexpr Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&& aOther) {
    downcast(aOther).reset();
    return *this;
  }

 private:
  MOZ_MAYBE_DOWNCAST()
};

template <typename T>
class Maybe_CopyMove_Enabler<T, truefalsetrue> {
 public:
  Maybe_CopyMove_Enabler() = default;

  Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete;
  Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete;
  constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) {
    downcast(aOther).reset();
  }
  constexpr Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&& aOther) {
    downcast(aOther).reset();
    return *this;
  }

 private:
  MOZ_MAYBE_DOWNCAST()
};

template <typename T>
class Maybe_CopyMove_Enabler<T, falsetruetrue> {
 public:
  Maybe_CopyMove_Enabler() = default;

  MOZ_MAYBE_COPY_OPS()
  MOZ_MAYBE_MOVE_OPS()

 private:
  MOZ_MAYBE_DOWNCAST()
};

template <typename T>
class Maybe_CopyMove_Enabler<T, falsefalsetrue> {
 public:
  Maybe_CopyMove_Enabler() = default;

  Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete;
  Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete;
  MOZ_MAYBE_MOVE_OPS()

 private:
  MOZ_MAYBE_DOWNCAST()
};

template <typename T>
class Maybe_CopyMove_Enabler<T, falsetruefalse> {
 public:
  Maybe_CopyMove_Enabler() = default;

  MOZ_MAYBE_COPY_OPS()
  Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&&) = delete;
  Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&&) = delete;

 private:
  MOZ_MAYBE_DOWNCAST()
};

template <typename T, bool TriviallyDestructibleAndCopyable>
class Maybe_CopyMove_Enabler<T, TriviallyDestructibleAndCopyable, false,
                             false> {
 public:
  Maybe_CopyMove_Enabler() = default;

  Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete;
  Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete;
  Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&&) = delete;
  Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&&) = delete;
};

#undef MOZ_MAYBE_COPY_OPS
#undef MOZ_MAYBE_MOVE_OPS
#undef MOZ_MAYBE_DOWNCAST

template <typename T, bool TriviallyDestructibleAndCopyable =
                          IsTriviallyDestructibleAndCopyable<T>>
struct MaybeStorage;

template <typename T>
struct MaybeStorage<T, false> : MaybeStorageBase<T> {
 protected:
  char mIsSome = false;  // not bool -- guarantees minimal space consumption

  constexpr MaybeStorage() = default;
  explicit MaybeStorage(const T& aVal)
      : MaybeStorageBase<T>{aVal}, mIsSome{true} {}
  explicit MaybeStorage(T&& aVal)
      : MaybeStorageBase<T>{std::move(aVal)}, mIsSome{true} {}

  template <typename... Args>
  explicit MaybeStorage(std::in_place_t, Args&&... aArgs)
      : MaybeStorageBase<T>{std::in_place, std::forward<Args>(aArgs)...},
        mIsSome{true} {}

 public:
  // Copy and move operations are no-ops, since copying is moving is implemented
  // by Maybe_CopyMove_Enabler.

  MaybeStorage(const MaybeStorage&) : MaybeStorageBase<T>{} {}
  MaybeStorage& operator=(const MaybeStorage&) { return *this; }
  MaybeStorage(MaybeStorage&&) : MaybeStorageBase<T>{} {}
  MaybeStorage& operator=(MaybeStorage&&) { return *this; }

  ~MaybeStorage() {
    if (mIsSome) {
      this->addr()->T::~T();
    }
  }
};

template <typename T>
struct MaybeStorage<T, true> : MaybeStorageBase<T> {
 protected:
  char mIsSome = false;  // not bool -- guarantees minimal space consumption

  constexpr MaybeStorage() = default;
  constexpr explicit MaybeStorage(const T& aVal)
      : MaybeStorageBase<T>{aVal}, mIsSome{true} {}
  constexpr explicit MaybeStorage(T&& aVal)
      : MaybeStorageBase<T>{std::move(aVal)}, mIsSome{true} {}

  template <typename... Args>
  constexpr explicit MaybeStorage(std::in_place_t, Args&&... aArgs)
      : MaybeStorageBase<T>{std::in_place, std::forward<Args>(aArgs)...},
        mIsSome{true} {}
};

template <typename T>
struct IsMaybeImpl : std::false_type {};

template <typename T>
struct IsMaybeImpl<Maybe<T>> : std::true_type {};

template <typename T>
using IsMaybe = IsMaybeImpl<std::decay_t<T>>;

}  // namespace detail

template <typename T, typename U = typename std::remove_cv<
                          typename std::remove_reference<T>::type>::type>
constexpr Maybe<U> Some(T&& aValue);

/*
 * 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 <typenameboolboolbool>
  friend class detail::Maybe_CopyMove_Enabler;

  template <typename U, typename V>
  friend constexpr Maybe<V> Some(U&& aValue);

  struct SomeGuard {};

  template <typename U>
  constexpr Maybe(U&& aValue, SomeGuard)
      : detail::MaybeStorage<T>{std::forward<U>(aValue)} {}

  using detail::MaybeStorage<T>::mIsSome;
  using detail::MaybeStorage<T>::mStorage;

  void poisonData() { detail::MaybePoisoner<T>::poison(&mStorage.val); }

 public:
  using ValueType = T;

  MOZ_ALLOW_TEMPORARY constexpr Maybe() = default;

  MOZ_ALLOW_TEMPORARY MOZ_IMPLICIT constexpr Maybe(Nothing) : Maybe{} {}

  template <typename... Args>
  constexpr explicit Maybe(std::in_place_t, Args&&... aArgs)
      : detail::MaybeStorage<T>{std::in_place, std::forward<Args>(aArgs)...} {}

  /**
   * 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);
    }
  }

  template <typename U, std::enable_if_t<!std::is_constructible_v<T, const U&>,
                                         bool> = true>
  explicit Maybe(const Maybe<U>& aOther) = delete;

  /**
   * 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;

  template <typename U,
            std::enable_if_t<std::is_constructible_v<T, const U&>, bool> = true>
  Maybe& operator=(const Maybe<U>& aOther) {
    if (aOther.isSome()) {
      if (mIsSome) {
        ref() = aOther.ref();
      } else {
        emplace(*aOther);
      }
    } else {
      reset();
    }
    return *this;
  }

  template <typename U, std::enable_if_t<!std::is_constructible_v<T, const U&>,
                                         bool> = true>
  Maybe& operator=(const Maybe<U>& aOther) = delete;

  template <typename U,
            std::enable_if_t<std::is_constructible_v<T, U&&>, bool> = true>
  Maybe& operator=(Maybe<U>&& aOther) {
    if (aOther.isSome()) {
      if (mIsSome) {
        ref() = std::move(aOther.ref());
      } else {
        emplace(std::move(*aOther));
      }
      aOther.reset();
    } else {
      reset();
    }

    return *this;
  }

  template <typename U,
            std::enable_if_t<!std::is_constructible_v<T, U&&>, bool> = true>
  Maybe& operator=(Maybe<U>&& aOther) = delete;

  constexpr Maybe& operator=(Nothing) {
    reset();
    return *this;
  }

  /* Methods that check whether this Maybe contains a value */
  constexpr explicit operator bool() 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;
  }

  constexpr const T* ptrOr(const T* aDefault) const {
    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();
  }

  template <typename F>
  const T* ptrOrFrom(F&& aFunc) const {
    if (isSome()) {
      return ptr();
    }
    return aFunc();
  }

  constexpr T* operator->();
  constexpr const T* operator->() const;

  /* 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;
  }

  constexpr const T& refOr(const T& aDefault) const {
    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();
  }

  template <typename F>
  constexpr const T& refOrFrom(F&& aFunc) const {
    if (isSome()) {
      return ref();
    }
    return aFunc();
  }

  constexpr T& operator*() &;
  constexpr const T& operator*() const&;
  constexpr T&& operator*() &&;
  constexpr const T&& operator*() const&&;

  /* 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;
  }

  template <typename Func>
  constexpr const Maybe& apply(Func&& aFunc) const& {
    if (isSome()) {
      std::forward<Func>(aFunc)(ref());
    }
    return *this;
  }

  template <typename Func>
  constexpr Maybe& apply(Func&& aFunc) && {
    if (isSome()) {
      std::forward<Func>(aFunc)(extract());
    }
    return *this;
  }

  template <typename Func>
  constexpr Maybe& apply(Func&& aFunc) const&& {
    if (isSome()) {
      std::forward<Func>(aFunc)(extract());
    }
    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);

  template <typename U>
  constexpr std::enable_if_t<std::is_same_v<T, U> &&
                             std::is_copy_constructible_v<U> &&
                             !std::is_move_constructible_v<U>>
  emplace(U&& aArgs) {
    emplace(aArgs);
  }

  friend std::ostream& operator<<(std::ostream& aStream,
                                  const Maybe<T>& aMaybe) {
    if (aMaybe) {
      aStream << aMaybe.ref();
    } else {
      aStream << "";
    }
    return aStream;
  }
};

template <typename T>
class Maybe<T&> {
 public:
  constexpr Maybe() = default;
  constexpr MOZ_IMPLICIT Maybe(Nothing) {}

  void emplace(T& aRef) { mValue = &aRef; }

  /* Methods that check whether this Maybe contains a value */
  constexpr explicit operator bool() const { return isSome(); }
  constexpr bool isSome() const { return mValue; }
  constexpr bool isNothing() const { return !mValue; }

  T& ref() const {
    MOZ_RELEASE_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 <typename Func>
  const Maybe& apply(Func&& aFunc) const {
    if (isSome()) {
      std::forward<Func>(aFunc)(ref());
    }
    return *this;
  }

  template <typename Func>
  auto map(Func&& aFunc) const {
    Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val;
    if (isSome()) {
      val.emplace(std::forward<Func>(aFunc)(ref()));
    }
    return val;
  }

  template <typename Func>
  constexpr auto andThen(Func&& aFunc) const {
    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 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));
  }

  bool refEquals(const Maybe<T&>& aOther) const {
    return mValue == aOther.mValue;
  }

  bool refEquals(const T& aOther) const { return mValue == &aOther; }

 private:
  T* mValue = nullptr;
};

template <typename T>
constexpr T Maybe<T>::value() const& {
  MOZ_RELEASE_ASSERT(isSome());
  return ref();
}

template <typename T>
constexpr T Maybe<T>::value() && {
  MOZ_RELEASE_ASSERT(isSome());
  return std::move(ref());
}

template <typename T>
constexpr T Maybe<T>::value() const&& {
  MOZ_RELEASE_ASSERT(isSome());
  return std::move(ref());
}

template <typename T>
T* Maybe<T>::ptr() {
  MOZ_RELEASE_ASSERT(isSome());
  return &ref();
}

template <typename T>
constexpr const T* Maybe<T>::ptr() const {
  MOZ_RELEASE_ASSERT(isSome());
  return &ref();
}

template <typename T>
constexpr T* Maybe<T>::operator->() {
  MOZ_RELEASE_ASSERT(isSome());
  return ptr();
}

template <typename T>
constexpr const T* Maybe<T>::operator->() const {
  MOZ_RELEASE_ASSERT(isSome());
  return ptr();
}

template <typename T>
constexpr T& Maybe<T>::ref() & {
  MOZ_RELEASE_ASSERT(isSome());
  return mStorage.val;
}

template <typename T>
constexpr const T& Maybe<T>::ref() const& {
  MOZ_RELEASE_ASSERT(isSome());
  return mStorage.val;
}

template <typename T>
constexpr T&& Maybe<T>::ref() && {
  MOZ_RELEASE_ASSERT(isSome());
  return std::move(mStorage.val);
}

template <typename T>
constexpr const T&& Maybe<T>::ref() const&& {
  MOZ_RELEASE_ASSERT(isSome());
  return std::move(mStorage.val);
}

template <typename T>
constexpr T& Maybe<T>::operator*() & {
  MOZ_RELEASE_ASSERT(isSome());
  return ref();
}

template <typename T>
constexpr const T& Maybe<T>::operator*() const& {
  MOZ_RELEASE_ASSERT(isSome());
  return ref();
}

template <typename T>
constexpr T&& Maybe<T>::operator*() && {
  MOZ_RELEASE_ASSERT(isSome());
  return std::move(ref());
}

template <typename T>
constexpr const T&& Maybe<T>::operator*() const&& {
  MOZ_RELEASE_ASSERT(isSome());
  return std::move(ref());
}

template <typename T>
template <typename... Args>
constexpr void Maybe<T>::emplace(Args&&... aArgs) {
  MOZ_RELEASE_ASSERT(!isSome());
  ::new (KnownNotNull, &mStorage.val) T(std::forward<Args>(aArgs)...);
  mIsSome = true;
}

/*
 * 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{}};
}

template <typename T>
constexpr Maybe<T&> SomeRef(T& aValue) {
  Maybe<T&> value;
  value.emplace(aValue);
  return value;
}

template <typename T>
constexpr Maybe<T&> ToMaybeRef(T* const aPtr) {
  return aPtr ? SomeRef(*aPtr) : Nothing{};
}

template <typename T>
Maybe<std::remove_cv_t<std::remove_reference_t<T>>> ToMaybe(T* aPtr) {
  if (aPtr) {
    return Some(*aPtr);
  }
  return Nothing();
}

/*
 * 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 bool operator==(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()) {
    return false;
  }
  return aLHS.isNothing() || *aLHS == *aRHS;
}

template <typename T>
constexpr bool operator!=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
  return !(aLHS == aRHS);
}

/*
 * We support comparison to Nothing to allow reasonable expressions like:
 *   if (maybeValue == Nothing()) { ... }
 */

template <typename T>
constexpr bool operator==(const Maybe<T>& aLHS, const Nothing& aRHS) {
  return aLHS.isNothing();
}

template <typename T>
constexpr bool operator!=(const Maybe<T>& aLHS, const Nothing& aRHS) {
  return !(aLHS == aRHS);
}

template <typename T>
constexpr bool operator==(const Nothing& aLHS, const Maybe<T>& aRHS) {
  return aRHS.isNothing();
}

template <typename T>
constexpr bool operator!=(const Nothing& aLHS, const Maybe<T>& aRHS) {
  return !(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 bool operator<(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
  if (aLHS.isNothing()) {
    return aRHS.isSome();
  }
  if (aRHS.isNothing()) {
    return false;
  }
  return *aLHS < *aRHS;
}

template <typename T>
constexpr bool operator>(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
  return !(aLHS < aRHS || aLHS == aRHS);
}

template <typename T>
constexpr bool operator<=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
  return aLHS < aRHS || aLHS == aRHS;
}

template <typename T>
constexpr bool operator>=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
  return !(aLHS < aRHS);
}

template <typename T>
inline void ImplCycleCollectionTraverse(
    nsCycleCollectionTraversalCallback& aCallback, mozilla::Maybe<T>& aField,
    const char* aName, uint32_t aFlags = 0) {
  if (aField) {
    ImplCycleCollectionTraverse(aCallback, aField.ref(), aName, aFlags);
  }
}

template <typename T>
inline void ImplCycleCollectionUnlink(mozilla::Maybe<T>& aField) {
  if (aField) {
    ImplCycleCollectionUnlink(aField.ref());
  }
}

}  // namespace mozilla

#endif /* mozilla_Maybe_h */

100%


¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge