Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/function2/include/function2/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 71 kB image not shown  

Quelle  function2.hpp   Sprache: C

 

//  Copyright 2015-2020 Denis Blank <denis.blank at outlook dot com>
//     Distributed under the Boost Software License, Version 1.0
//       (See accompanying file LICENSE_1_0.txt or copy at
//             http://www.boost.org/LICENSE_1_0.txt)

#ifndef FU2_INCLUDED_FUNCTION2_HPP_
#define FU2_INCLUDED_FUNCTION2_HPP_

#include <cassert>
#include <cstddef>
#include <cstdlib>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>

// Defines:
// - FU2_HAS_DISABLED_EXCEPTIONS
#if defined(FU2_WITH_DISABLED_EXCEPTIONS) ||                                   \
    defined(FU2_MACRO_DISABLE_EXCEPTIONS)
#define FU2_HAS_DISABLED_EXCEPTIONS
#else // FU2_WITH_DISABLED_EXCEPTIONS
#if defined(_MSC_VER)
#if !defined(_HAS_EXCEPTIONS) || (_HAS_EXCEPTIONS == 0)
#define FU2_HAS_DISABLED_EXCEPTIONS
#endif
#elif defined(__clang__)
#if !(__EXCEPTIONS && __has_feature(cxx_exceptions))
#define FU2_HAS_DISABLED_EXCEPTIONS
#endif
#elif defined(__GNUC__)
#if !__EXCEPTIONS
#define FU2_HAS_DISABLED_EXCEPTIONS
#endif
#endif
#endif // FU2_WITH_DISABLED_EXCEPTIONS
// - FU2_HAS_LIMITED_EMPTY_PROPAGATION
#if defined(FU2_WITH_LIMITED_EMPTY_PROPAGATION)
#define FU2_HAS_LIMITED_EMPTY_PROPAGATION
#endif // FU2_WITH_NO_EMPTY_PROPAGATION
// - FU2_HAS_NO_FUNCTIONAL_HEADER
#if !defined(FU2_WITH_NO_FUNCTIONAL_HEADER) &&                                 \
    !defined(FU2_NO_FUNCTIONAL_HEADER) &&                                      \
    (!defined(FU2_HAS_DISABLED_EXCEPTIONS) ||                                  \
     defined(FU2_HAS_LIMITED_EMPTY_PROPAGATION))
#include <functional>
#else
#define FU2_HAS_NO_FUNCTIONAL_HEADER
#endif
// - FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#if defined(FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE)
#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#else // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE
#if defined(_MSC_VER)
#if defined(_HAS_CXX17) && _HAS_CXX17
#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#endif
#elif defined(__cpp_noexcept_function_type)
#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#elif defined(__cplusplus) && (__cplusplus >= 201703L)
#define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#endif
#endif // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE

// - FU2_HAS_NO_EMPTY_PROPAGATION
#if defined(FU2_WITH_NO_EMPTY_PROPAGATION)
#define FU2_HAS_NO_EMPTY_PROPAGATION
#endif // FU2_WITH_NO_EMPTY_PROPAGATION

#if !defined(FU2_HAS_DISABLED_EXCEPTIONS)
#include <exception>
#endif

#if defined(__cpp_constexpr) && (__cpp_constexpr >= 201304)
#define FU2_DETAIL_CXX14_CONSTEXPR constexpr
#elif defined(__clang__) && defined(__has_feature)
#if __has_feature(__cxx_generic_lambdas__) &&                                  \
    __has_feature(__cxx_relaxed_constexpr__)
#define FU2_DETAIL_CXX14_CONSTEXPR constexpr
#endif
#elif defined(_MSC_VER) && (_MSC_VER >= 1915) && (_MSVC_LANG >= 201402)
#define FU2_DETAIL_CXX14_CONSTEXPR constexpr
#endif
#ifndef FU2_DETAIL_CXX14_CONSTEXPR
#define FU2_DETAIL_CXX14_CONSTEXPR
#endif

/// Hint for the compiler that this point should be unreachable
#if defined(_MSC_VER)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __assume(false)
#elif defined(__GNUC__)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable()
#elif defined(__has_builtin)
#if __has_builtin(__builtin_unreachable)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable()
#endif
#endif
#ifndef FU2_DETAIL_UNREACHABLE_INTRINSIC
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE_INTRINSIC() abort()
#endif

/// Causes the application to exit abnormally
#if defined(_MSC_VER)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_TRAP() __debugbreak()
#elif defined(__GNUC__)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_TRAP() __builtin_trap()
#elif defined(__has_builtin)
#if __has_builtin(__builtin_trap)
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_TRAP() __builtin_trap()
#endif
#endif
#ifndef FU2_DETAIL_TRAP
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_TRAP() *(volatile int*)0x11 = 0
#endif

#ifndef NDEBUG
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE() ::fu2::detail::unreachable_debug()
#else
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define FU2_DETAIL_UNREACHABLE() FU2_DETAIL_UNREACHABLE_INTRINSIC()
#endif

namespace fu2 {
inline namespace abi_400 {
namespace detail {
template <typename Config, typename Property>
class function;

template <typename...>
struct identity {};

// Equivalent to C++17's std::void_t which targets a bug in GCC,
// that prevents correct SFINAE behavior.
// See http://stackoverflow.com/questions/35753920 for details.
template <typename...>
struct deduce_to_void : std::common_type<void> {};

template <typename... T>
using void_t = typename deduce_to_void<T...>::type;

template <typename T>
using unrefcv_t = std::remove_cv_t<std::remove_reference_t<T>>;

template <typename...>
struct lazy_and;

template <typename B1>
struct lazy_and<B1> : B1 {};

template <typename B1, typename B2>
struct lazy_and<B1, B2> : std::conditional<B1::value, B2, B1>::type {};

// template <typename B1, typename B2, typename B3, typename... Bn>
// struct lazy_and<B1, B2, B3, Bn...>
//     : std::conditional<B1::value, lazy_and<B2, B3, Bn...>, B1>::type {};

// Copy enabler helper class
template <bool /*Copyable*/>
struct copyable {};
template <>
struct copyable<false> {
  copyable() = default;
  ~copyable() = default;
  copyable(copyable const&) = delete;
  copyable(copyable&&) = default;
  copyable& operator=(copyable const&) = delete;
  copyable& operator=(copyable&&) = default;
};

/// Configuration trait to configure the function_base class.
template <bool Owning, bool Copyable, typename Capacity>
struct config {
  // Is true if the function is owning.
  static constexpr auto const is_owning = Owning;

  // Is true if the function is copyable.
  static constexpr auto const is_copyable = Copyable;

  // The internal capacity of the function
  // used in small functor optimization.
  // The object shall expose the real capacity through Capacity::capacity
  // and the intended alignment through Capacity::alignment.
  using capacity = Capacity;
};

/// A config which isn't compatible to other configs
template <bool Throws, bool HasStrongExceptGuarantee, typename... Args>
struct property {
  // Is true when the function throws an exception on empty invocation.
  static constexpr auto const is_throwing = Throws;

  // Is true when the function throws an exception on empty invocation.
  static constexpr auto const is_strong_exception_guaranteed =
      HasStrongExceptGuarantee;
};

#ifndef NDEBUG
[[noreturn]] inline void unreachable_debug() {
  FU2_DETAIL_TRAP();
  std::abort();
}
#endif

/// Provides utilities for invocing callable objects
namespace invocation {
/// Invokes the given callable object with the given arguments
template <typename Callable, typename... Args>
constexpr auto invoke(Callable&& callable, Args&&... args) noexcept(
    noexcept(std::forward<Callable>(callable)(std::forward<Args>(args)...)))
    -> decltype(std::forward<Callable>(callable)(std::forward<Args>(args)...)) {

  return std::forward<Callable>(callable)(std::forward<Args>(args)...);
}
/// Invokes the given member function pointer by reference
template <typename T, typename Type, typename Self, typename... Args>
constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept(
    noexcept((std::forward<Self>(self).*member)(std::forward<Args>(args)...)))
    -> decltype((std::forward<Self>(self).*
                 member)(std::forward<Args>(args)...)) {
  return (std::forward<Self>(self).*member)(std::forward<Args>(args)...);
}
/// Invokes the given member function pointer by pointer
template <typename T, typename Type, typename Self, typename... Args>
constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept(
    noexcept((std::forward<Self>(self)->*member)(std::forward<Args>(args)...)))
    -> decltype((std::forward<Self>(self)->*member)(
        std::forward<Args>(args)...)) {
  return (std::forward<Self>(self)->*member)(std::forward<Args>(args)...);
}
/// Invokes the given pointer to a scalar member by reference
template <typename T, typename Type, typename Self>
constexpr auto
invoke(Type T::*member,
       Self&& self) noexcept(noexcept(std::forward<Self>(self).*member))
    -> decltype(std::forward<Self>(self).*member) {
  return (std::forward<Self>(self).*member);
}
/// Invokes the given pointer to a scalar member by pointer
template <typename T, typename Type, typename Self>
constexpr auto
invoke(Type T::*member,
       Self&& self) noexcept(noexcept(std::forward<Self>(self)->*member))
    -> decltype(std::forward<Self>(self)->*member) {
  return std::forward<Self>(self)->*member;
}

/// Deduces to a true type if the callable object can be invoked with
/// the given arguments.
/// We don't use invoke here because MSVC can't evaluate the nested expression
/// SFINAE here.
template <typename T, typename Args, typename = void>
struct can_invoke : std::false_type {};
template <typename T, typename... Args>
struct can_invoke<T, identity<Args...>,
                  decltype((void)std::declval<T>()(std::declval<Args>()...))>
    : std::true_type {};
template <typename Pointer, typename T, typename... Args>
struct can_invoke<Pointer, identity<T&, Args...>,
                  decltype((void)((std::declval<T&>().*std::declval<Pointer>())(
                      std::declval<Args>()...)))> : std::true_type {};
template <typename Pointer, typename T, typename... Args>
struct can_invoke<Pointer, identity<T&&, Args...>,
                  decltype((
                      void)((std::declval<T&&>().*std::declval<Pointer>())(
                      std::declval<Args>()...)))> : std::true_type {};
template <typename Pointer, typename T, typename... Args>
struct can_invoke<Pointer, identity<T*, Args...>,
                  decltype((
                      void)((std::declval<T*>()->*std::declval<Pointer>())(
                      std::declval<Args>()...)))> : std::true_type {};
template <typename Pointer, typename T>
struct can_invoke<Pointer, identity<T&>,
                  decltype((void)(std::declval<T&>().*std::declval<Pointer>()))>
    : std::true_type {};
template <typename Pointer, typename T>
struct can_invoke<Pointer, identity<T&&>,
                  decltype((void)(std::declval<T&&>().*
                                  std::declval<Pointer>()))> : std::true_type {
};
template <typename Pointer, typename T>
struct can_invoke<Pointer, identity<T*>,
                  decltype((
                      void)(std::declval<T*>()->*std::declval<Pointer>()))>
    : std::true_type {};

template <bool RequiresNoexcept, typename T, typename Args>
struct is_noexcept_correct : std::true_type {};
template <typename T, typename... Args>
struct is_noexcept_correct<true, T, identity<Args...>>
    : std::integral_constant<bool,
                             noexcept(::fu2::detail::invocation::invoke(
                                 std::declval<T>(), std::declval<Args>()...))> {
};
// end namespace invocation

namespace overloading {
template <typename... Args>
struct overload_impl;
template <typename Current, typename Next, typename... Rest>
struct overload_impl<Current, Next, Rest...> : Current,
                                               overload_impl<Next, Rest...> {
  explicit overload_impl(Current current, Next next, Rest... rest)
      : Current(std::move(current)),
        overload_impl<Next, Rest...>(std::move(next), std::move(rest)...) {
  }

  using Current::operator();
  using overload_impl<Next, Rest...>::operator();
};
template <typename Current>
struct overload_impl<Current> : Current {
  explicit overload_impl(Current current) : Current(std::move(current)) {
  }

  using Current::operator();
};

template <typename... T>
constexpr auto overload(T&&... callables) {
  return overload_impl<std::decay_t<T>...>{std::forward<T>(callables)...};
}
// namespace overloading

/// Declares the namespace which provides the functionality to work with a
/// type-erased object.
namespace type_erasure {
/// Specialization to work with addresses of callable objects
template <typename T, typename = void>
struct address_taker {
  template <typename O>
  static auto take(O&& obj) {
    return std::addressof(obj);
  }
  static T& restore(void* ptr) {
    return *static_cast<T*>(ptr);
  }
  static T const& restore(void const* ptr) {
    return *static_cast<T const*>(ptr);
  }
  static T volatile& restore(void volatile* ptr) {
    return *static_cast<T volatile*>(ptr);
  }
  static T const volatile& restore(void const volatile* ptr) {
    return *static_cast<T const volatile*>(ptr);
  }
};
/// Specialization to work with addresses of raw function pointers
template <typename T>
struct address_taker<T, std::enable_if_t<std::is_pointer<T>::value>> {
  template <typename O>
  static void* take(O&& obj) {
    return reinterpret_cast<void*>(obj);
  }
  template <typename O>
  static T restore(O ptr) {
    return reinterpret_cast<T>(const_cast<void*>(ptr));
  }
};

template <typename Box>
struct box_factory;
/// Store the allocator inside the box
template <bool IsCopyable, typename T, typename Allocator>
struct box : private Allocator {
  friend box_factory<box>;

  T value_;

  explicit box(T value, Allocator allocator_)
      : Allocator(std::move(allocator_)), value_(std::move(value)) {
  }

  box(box&&) = default;
  box(box const&) = default;
  box& operator=(box&&) = default;
  box& operator=(box const&) = default;
  ~box() = default;
};
template <typename T, typename Allocator>
struct box<false, T, Allocator> : private Allocator {
  friend box_factory<box>;

  T value_;

  explicit box(T value, Allocator allocator_)
      : Allocator(std::move(allocator_)), value_(std::move(value)) {
  }

  box(box&&) = default;
  box(box const&) = delete;
  box& operator=(box&&) = default;
  box& operator=(box const&) = delete;
  ~box() = default;
};

template <bool IsCopyable, typename T, typename Allocator>
struct box_factory<box<IsCopyable, T, Allocator>> {
  using real_allocator =
      typename std::allocator_traits<std::decay_t<Allocator>>::
          template rebind_alloc<box<IsCopyable, T, Allocator>>;

  /// Allocates space through the boxed allocator
  static box<IsCopyable, T, Allocator>*
  box_allocate(box<IsCopyable, T, Allocator> const* me) {
    real_allocator allocator_(*static_cast<Allocator const*>(me));

    return static_cast<box<IsCopyable, T, Allocator>*>(
        std::allocator_traits<real_allocator>::allocate(allocator_, 1U));
  }

  /// Destroys the box through the given allocator
  static void box_deallocate(box<IsCopyable, T, Allocator>* me) {
    real_allocator allocator_(*static_cast<Allocator const*>(me));

    me->~box();
    std::allocator_traits<real_allocator>::deallocate(allocator_, me, 1U);
  }
};

/// Creates a box containing the given value and allocator
template <bool IsCopyable, typename T, typename Allocator>
auto make_box(std::integral_constant<bool, IsCopyable>, T&& value,
              Allocator&& allocator_) {
  return box<IsCopyable, std::decay_t<T>, std::decay_t<Allocator>>(
      std::forward<T>(value), std::forward<Allocator>(allocator_));
}

template <typename T>
struct is_box : std::false_type {};
template <bool IsCopyable, typename T, typename Allocator>
struct is_box<box<IsCopyable, T, Allocator>> : std::true_type {};

/// Provides access to the pointer to a heal allocated erased object
/// as well to the inplace storage.
union data_accessor {
  data_accessor() = default;
  explicit constexpr data_accessor(std::nullptr_t) noexcept : ptr_(nullptr) {
  }
  explicit constexpr data_accessor(void* ptr) noexcept : ptr_(ptr) {
  }
  explicit constexpr data_accessor(void const* ptr) noexcept
      : data_accessor(const_cast<void*>(ptr)) {
  }

  constexpr void assign_ptr(void* ptr) noexcept {
    ptr_ = ptr;
  }
  constexpr void assign_ptr(void const* ptr) noexcept {
    ptr_ = const_cast<void*>(ptr);
  }

  /// The pointer we use if the object is on the heap
  void* ptr_;
  /// The first field of the inplace storage
  std::size_t inplace_storage_;
};

/// See opcode::op_fetch_empty
static FU2_DETAIL_CXX14_CONSTEXPR void write_empty(data_accessor* accessor,
                                                   bool empty) noexcept {
  accessor->inplace_storage_ = std::size_t(empty);
}

template <typename From, typename To>
using transfer_const_t =
    std::conditional_t<std::is_const<std::remove_pointer_t<From>>::value,
                       std::add_const_t<To>, To>;
template <typename From, typename To>
using transfer_volatile_t =
    std::conditional_t<std::is_volatile<std::remove_pointer_t<From>>::value,
                       std::add_volatile_t<To>, To>;

/// The retriever when the object is allocated inplace
template <typename T, typename Accessor>
FU2_DETAIL_CXX14_CONSTEXPR auto retrieve(std::true_type /*is_inplace*/,
                                         Accessor from,
                                         std::size_t from_capacity) {
  using type = transfer_const_t<Accessor, transfer_volatile_t<Accessor, void>>*;

  /// Process the command by using the data inside the internal capacity
  auto storage = &(from->inplace_storage_);
  auto inplace = const_cast<void*>(static_cast<type>(storage));
  return type(std::align(alignof(T), sizeof(T), inplace, from_capacity));
}

/// The retriever which is used when the object is allocated
/// through the allocator
template <typename T, typename Accessor>
constexpr auto retrieve(std::false_type /*is_inplace*/, Accessor from,
                        std::size_t /*from_capacity*/) {

  return from->ptr_;
}

namespace invocation_table {
#if !defined(FU2_HAS_DISABLED_EXCEPTIONS)
#if defined(FU2_HAS_NO_FUNCTIONAL_HEADER)
struct bad_function_call : std::exception {
  bad_function_call() noexcept {
  }

  char const* what() const noexcept override {
    return "bad function call";
  }
};
#else
using std::bad_function_call;
#endif
#endif

#ifdef FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#define FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F)                               \
  F(, , noexcept, , &)                                                         \
  F(const, , noexcept, , &)                                                    \
  F(, volatile, noexcept, , &)                                                 \
  F(constvolatile, noexcept, , &)                                            \
  F(, , noexcept, &, &)                                                        \
  F(const, , noexcept, &, &)                                                   \
  F(, volatile, noexcept, &, &)                                                \
  F(constvolatile, noexcept, &, &)                                           \
  F(, , noexcept, &&, &&)                                                      \
  F(const, , noexcept, &&, &&)                                                 \
  F(, volatile, noexcept, &&, &&)                                              \
  F(constvolatile, noexcept, &&, &&)
#define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F)                                       \
  F(, , noexcept)                                                              \
  F(const, , noexcept)                                                         \
  F(, volatile, noexcept)                                                      \
  F(constvolatile, noexcept)
#else // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
#define FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F)
#define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F)
#endif // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE

#define FU2_DETAIL_EXPAND_QUALIFIERS(F)                                        \
  F(, , , , &)                                                                 \
  F(const, , , , &)                                                            \
  F(, volatile, , , &)                                                         \
  F(constvolatile, , , &)                                                    \
  F(, , , &, &)                                                                \
  F(const, , , &, &)                                                           \
  F(, volatile, , &, &)                                                        \
  F(constvolatile, , &, &)                                                   \
  F(, , , &&, &&)                                                              \
  F(const, , , &&, &&)                                                         \
  F(, volatile, , &&, &&)                                                      \
  F(constvolatile, , &&, &&)                                                 \
  FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F)
#define FU2_DETAIL_EXPAND_CV(F)                                                \
  F(, , )                                                                      \
  F(const, , )                                                                 \
  F(, volatile, )                                                              \
  F(constvolatile, )                                                         \
  FU2_DETAIL_EXPAND_CV_NOEXCEPT(F)

/// If the function is qualified as noexcept, the call will never throw
template <bool IsNoexcept>
[[noreturn]] void throw_or_abortnoexcept(
    std::integral_constant<bool, IsNoexcept> /*is_throwing*/) noexcept {
  std::abort();
}
/// Calls std::abort on empty function calls
[[noreturn]] inline void
throw_or_abort(std::false_type /*is_throwing*/) noexcept {
  std::abort();
}
/// Throws bad_function_call on empty funciton calls
[[noreturn]] inline void throw_or_abort(std::true_type /*is_throwing*/) {
#ifdef FU2_HAS_DISABLED_EXCEPTIONS
  throw_or_abort(std::false_type{});
#else
  throw bad_function_call{};
#endif
}

template <typename T>
struct function_trait;

using is_noexcept_ = std::false_type;
using is_noexcept_noexcept = std::true_type;

#define FU2_DEFINE_FUNCTION_TRAIT(CONSTVOLATILE, NOEXCEPT, OVL_REF, REF)     \
  template <typename Ret, typename... Args>                                    \
  struct function_trait<Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT> {        \
    using pointer_type = Ret (*)(data_accessor CONST VOLATILE*,                \
                                 std::size_t capacity, Args...);               \
    template <typename T, bool IsInplace>                                      \
    struct internal_invoker {                                                  \
      static Ret invoke(data_accessor CONST VOLATILE* data,                    \
                        std::size_t capacity, Args... args) NOEXCEPT {         \
        auto obj = retrieve<T>(std::integral_constant<bool, IsInplace>{},      \
                               data, capacity);                                \
        auto box = static_cast<T CONST VOLATILE*>(obj);                        \
        return invocation::invoke(                                             \
            static_cast<std::decay_t<decltype(box->value_)> CONST VOLATILE     \
                            REF>(box->value_),                                 \
            std::forward<Args>(args)...);                                      \
      }                                                                        \
    };                                                                         \
                                                                               \
    template <typename T>                                                      \
    struct view_invoker {                                                      \
      static Ret invoke(data_accessor CONST VOLATILE* data, std::size_t,       \
                        Args... args) NOEXCEPT {                               \
                                                                               \
        auto ptr = static_cast<void CONST VOLATILE*>(data->ptr_);              \
        return invocation::invoke(address_taker<T>::restore(ptr),              \
                                  std::forward<Args>(args)...);                \
      }                                                                        \
    };                                                                         \
                                                                               \
    template <typename T>                                                      \
    using callable = T CONST VOLATILE REF;                                     \
                                                                               \
    using arguments = identity<Args...>;                                       \
                                                                               \
    using is_noexcept = is_noexcept_##NOEXCEPT;                                \
                                                                               \
    template <bool Throws>                                                     \
    struct empty_invoker {                                                     \
      static Ret invoke(data_accessor CONST VOLATILE/*data*/,                \
                        std::size_t /*capacity*/, Args... /*args*/) NOEXCEPT { \
        throw_or_abort##NOEXCEPT(std::integral_constant<bool, Throws>{});      \
      }                                                                        \
    };                                                                         \
  };

FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT)
#undef FU2_DEFINE_FUNCTION_TRAIT

/// Deduces to the function pointer to the given signature
template <typename Signature>
using function_pointer_of = typename function_trait<Signature>::pointer_type;

template <typename... Args>
struct invoke_table;

/// We optimize the vtable_t in case there is a single function overload
template <typename First>
struct invoke_table<First> {
  using type = function_pointer_of<First>;

  /// Return the function pointer itself
  template <std::size_t Index>
  static constexpr auto fetch(type pointer) noexcept {
    static_assert(Index == 0U, "The index should be 0 here!");
    return pointer;
  }

  /// Returns the thunk of an single overloaded callable
  template <typename T, bool IsInplace>
  static constexpr type get_invocation_table_of() noexcept {
    return &function_trait<First>::template internal_invoker<T,
                                                             IsInplace>::invoke;
  }
  /// Returns the thunk of an single overloaded callable
  template <typename T>
  static constexpr type get_invocation_view_table_of() noexcept {
    return &function_trait<First>::template view_invoker<T>::invoke;
  }
  /// Returns the thunk of an empty single overloaded callable
  template <bool IsThrowing>
  static constexpr type get_empty_invocation_table() noexcept {
    return &function_trait<First>::template empty_invoker<IsThrowing>::invoke;
  }
};
/// We generate a table in case of multiple function overloads
template <typename First, typename Second, typename... Args>
struct invoke_table<First, Second, Args...> {
  using type =
      std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
                 function_pointer_of<Args>...> const*;

  /// Return the function pointer at the particular index
  template <std::size_t Index>
  static constexpr auto fetch(type table) noexcept {
    return std::get<Index>(*table);
  }

  /// The invocation vtable for a present object
  template <typename T, bool IsInplace>
  struct invocation_vtable : public std::tuple<function_pointer_of<First>,
                                               function_pointer_of<Second>,
                                               function_pointer_of<Args>...> {
    constexpr invocation_vtable() noexcept
        : std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
                     function_pointer_of<Args>...>(std::make_tuple(
              &function_trait<First>::template internal_invoker<
                  T, IsInplace>::invoke,
              &function_trait<Second>::template internal_invoker<
                  T, IsInplace>::invoke,
              &function_trait<Args>::template internal_invoker<
                  T, IsInplace>::invoke...)) {
    }
  };

  /// Returns the thunk of an multi overloaded callable
  template <typename T, bool IsInplace>
  static type get_invocation_table_of() noexcept {
    static invocation_vtable<T, IsInplace> const table;
    return &table;
  }

  /// The invocation vtable for a present object
  template <typename T>
  struct invocation_view_vtable
      : public std::tuple<function_pointer_of<First>,
                          function_pointer_of<Second>,
                          function_pointer_of<Args>...> {
    constexpr invocation_view_vtable() noexcept
        : std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
                     function_pointer_of<Args>...>(std::make_tuple(
              &function_trait<First>::template view_invoker<T>::invoke,
              &function_trait<Second>::template view_invoker<T>::invoke,
              &function_trait<Args>::template view_invoker<T>::invoke...)) {
    }
  };

  /// Returns the thunk of an multi overloaded callable
  template <typename T>
  static type get_invocation_view_table_of() noexcept {
    static invocation_view_vtable<T> const table;
    return &table;
  }

  /// The invocation table for an empty wrapper
  template <bool IsThrowing>
  struct empty_vtable : public std::tuple<function_pointer_of<First>,
                                          function_pointer_of<Second>,
                                          function_pointer_of<Args>...> {
    constexpr empty_vtable() noexcept
        : std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
                     function_pointer_of<Args>...>(
              std::make_tuple(&function_trait<First>::template empty_invoker<
                                  IsThrowing>::invoke,
                              &function_trait<Second>::template empty_invoker<
                                  IsThrowing>::invoke,
                              &function_trait<Args>::template empty_invoker<
                                  IsThrowing>::invoke...)) {
    }
  };

  /// Returns the thunk of an multi single overloaded callable
  template <bool IsThrowing>
  static type get_empty_invocation_table() noexcept {
    static empty_vtable<IsThrowing> const table;
    return &table;
  }
};

template <std::size_t Index, typename Function, typename... Signatures>
class operator_impl;

#define FU2_DEFINE_FUNCTION_TRAIT(CONSTVOLATILE, NOEXCEPT, OVL_REF, REF)     \
  template <std::size_t Index, typename Function, typename Ret,                \
            typename... Args, typename Next, typename... Signatures>           \
  class operator_impl<Index, Function,                                         \
                      Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT, Next,      \
                      Signatures...>                                           \
      : operator_impl<Index + 1, Function, Next, Signatures...> {              \
                                                                               \
    template <std::size_t, typenametypename...>                              \
    friend class operator_impl;                                                \
                                                                               \
  protected:                                                                   \
    operator_impl() = default;                                                 \
    ~operator_impl() = default;                                                \
    operator_impl(operator_impl const&) = default;                             \
    operator_impl(operator_impl&&) = default;                                  \
    operator_impl& operator=(operator_impl const&) = default;                  \
    operator_impl& operator=(operator_impl&&) = default;                       \
                                                                               \
    using operator_impl<Index + 1, Function, Next, Signatures...>::operator(); \
                                                                               \
    Ret operator()(Args... args) CONST VOLATILE OVL_REF NOEXCEPT {             \
      auto parent = static_cast<Function CONST VOLATILE*>(this);               \
      using erasure_t = std::decay_t<decltype(parent->erasure_)>;              \
                                                                               \
      /* `std::decay_t<decltype(parent->erasure_)>` is a workaround for a   */ \
      /* compiler regression of MSVC 16.3.1, see #29 for details.           */ \
      return std::decay_t<decltype(parent->erasure_)>::template invoke<Index>( \
          static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_),         \
          std::forward<Args>(args)...);                                        \
    }                                                                          \
  };                                                                           \
  template <std::size_t Index, typename Config, typename Property,             \
            typename Ret, typename... Args>                                    \
  class operator_impl<Index, function<Config, Property>,                       \
                      Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT>            \
      : copyable<!Config::is_owning || Config::is_copyable> {                  \
                                                                               \
    template <std::size_t, typenametypename...>                              \
    friend class operator_impl;                                                \
                                                                               \
  protected:                                                                   \
    operator_impl() = default;                                                 \
    ~operator_impl() = default;                                                \
    operator_impl(operator_impl const&) = default;                             \
    operator_impl(operator_impl&&) = default;                                  \
    operator_impl& operator=(operator_impl const&) = default;                  \
    operator_impl& operator=(operator_impl&&) = default;                       \
                                                                               \
    Ret operator()(Args... args) CONST VOLATILE OVL_REF NOEXCEPT {             \
      auto parent =                                                            \
          static_cast<function<Config, Property> CONST VOLATILE*>(this);       \
      using erasure_t = std::decay_t<decltype(parent->erasure_)>;              \
                                                                               \
      /* `std::decay_t<decltype(parent->erasure_)>` is a workaround for a   */ \
      /* compiler regression of MSVC 16.3.1, see #29 for details.           */ \
      return std::decay_t<decltype(parent->erasure_)>::template invoke<Index>( \
          static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_),         \
          std::forward<Args>(args)...);                                        \
    }                                                                          \
  };

FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT)
#undef FU2_DEFINE_FUNCTION_TRAIT
// namespace invocation_table

namespace tables {
/// Identifies the action which is dispatched on the erased object
enum class opcode {
  op_move,         ///< Move the object and set the vtable
  op_copy,         ///< Copy the object and set the vtable
  op_destroy,      ///< Destroy the object and reset the vtable
  op_weak_destroy, ///< Destroy the object without resetting the vtable
  op_fetch_empty,  ///< Stores true or false into the to storage
                   ///< to indicate emptiness
};

/// Abstraction for a vtable together with a command table
/// TODO Add optimization for a single formal argument
/// TODO Add optimization to merge both tables if the function is size
/// optimized
template <typename Property>
class vtable;
template <bool IsThrowing, bool HasStrongExceptGuarantee,
          typename... FormalArgs>
class vtable<property<IsThrowing, HasStrongExceptGuarantee, FormalArgs...>> {
  using command_function_t = void (*)(vtable* /*this*/, opcode /*op*/,
                                      data_accessor* /*from*/,
                                      std::size_t /*from_capacity*/,
                                      data_accessor* /*to*/,
                                      std::size_t /*to_capacity*/);

  using invoke_table_t = invocation_table::invoke_table<FormalArgs...>;

  command_function_t cmd_;
  typename invoke_table_t::type vtable_;

  template <typename T>
  struct trait {
    static_assert(is_box<T>::value,
                  "The trait must be specialized with a box!");

    /// The command table
    template <bool IsInplace>
    static void process_cmd(vtable* to_table, opcode op, data_accessor* from,
                            std::size_t from_capacity, data_accessor* to,
                            std::size_t to_capacity) {

      switch (op) {
        case opcode::op_move: {
          /// Retrieve the pointer to the object
          auto box = static_cast<T*>(retrieve<T>(
              std::integral_constant<bool, IsInplace>{}, from, from_capacity));
          assert(box && "The object must not be over aligned or null!");

          if (!IsInplace) {
            // Just swap both pointers if we allocated on the heap
            to->ptr_ = from->ptr_;

#ifndef NDEBUG
            // We don't need to null the pointer since we know that
            // we don't own the data anymore through the vtable
            // which is set to empty.
            from->ptr_ = nullptr;
#endif

            to_table->template set_allocated<T>();

          }
          // The object is allocated inplace
          else {
            construct(std::true_type{}, std::move(*box), to_table, to,
                      to_capacity);
            box->~T();
          }
          return;
        }
        case opcode::op_copy: {
          auto box = static_cast<T const*>(retrieve<T>(
              std::integral_constant<bool, IsInplace>{}, from, from_capacity));
          assert(box && "The object must not be over aligned or null!");

          assert(std::is_copy_constructible<T>::value &&
                 "The box is required to be copyable here!");

          // Try to allocate the object inplace
          construct(std::is_copy_constructible<T>{}, *box, to_table, to,
                    to_capacity);
          return;
        }
        case opcode::op_destroy:
        case opcode::op_weak_destroy: {

          assert(!to && !to_capacity && "Arg overflow!");
          auto box = static_cast<T*>(retrieve<T>(
              std::integral_constant<bool, IsInplace>{}, from, from_capacity));

          if (IsInplace) {
            box->~T();
          } else {
            box_factory<T>::box_deallocate(box);
          }

          if (op == opcode::op_destroy) {
            to_table->set_empty();
          }
          return;
        }
        case opcode::op_fetch_empty: {
          write_empty(to, false);
          return;
        }
      }

      FU2_DETAIL_UNREACHABLE();
    }

    template <typename Box>
    static void
    construct(std::true_type /*apply*/, Box&& box, vtable* to_table,
              data_accessor* to,
              std::size_t to_capacity) noexcept(HasStrongExceptGuarantee) {
      // Try to allocate the object inplace
      void* storage = retrieve<T>(std::true_type{}, to, to_capacity);
      if (storage) {
        to_table->template set_inplace<T>();
      } else {
        // Allocate the object through the allocator
        to->ptr_ = storage =
            box_factory<std::decay_t<Box>>::box_allocate(std::addressof(box));
        to_table->template set_allocated<T>();
      }
      new (storage) T(std::forward<Box>(box));
    }

    template <typename Box>
    static void
    construct(std::false_type /*apply*/, Box&& /*box*/, vtable* /*to_table*/,
              data_accessor* /*to*/,
              std::size_t /*to_capacity*/) noexcept(HasStrongExceptGuarantee) {
    }
  };

  /// The command table
  static void empty_cmd(vtable* to_table, opcode op, data_accessor* /*from*/,
                        std::size_t /*from_capacity*/, data_accessor* to,
                        std::size_t /*to_capacity*/) {

    switch (op) {
      case opcode::op_move:
      case opcode::op_copy: {
        to_table->set_empty();
        break;
      }
      case opcode::op_destroy:
      case opcode::op_weak_destroy: {
        // Do nothing
        break;
      }
      case opcode::op_fetch_empty: {
        write_empty(to, true);
        break;
      }
      default: {
        FU2_DETAIL_UNREACHABLE();
      }
    }
  }

public:
  vtable() noexcept = default;

  /// Initialize an object at the given position
  template <typename T>
  static void init(vtable& table, T&& object, data_accessor* to,
                   std::size_t to_capacity) {

    trait<std::decay_t<T>>::construct(std::true_type{}, std::forward<T>(object),
                                      &table, to, to_capacity);
  }

  /// Moves the object at the given position
  void move(vtable& to_table, data_accessor* from, std::size_t from_capacity,
            data_accessor* to,
            std::size_t to_capacity) noexcept(HasStrongExceptGuarantee) {
    cmd_(&to_table, opcode::op_move, from, from_capacity, to, to_capacity);
    set_empty();
  }

  /// Destroys the object at the given position
  void copy(vtable& to_table, data_accessor const* from,
            std::size_t from_capacity, data_accessor* to,
            std::size_t to_capacity) const {
    cmd_(&to_table, opcode::op_copy, const_cast<data_accessor*>(from),
         from_capacity, to, to_capacity);
  }

  /// Destroys the object at the given position
  void destroy(data_accessor* from,
               std::size_t from_capacity) noexcept(HasStrongExceptGuarantee) {
    cmd_(this, opcode::op_destroy, from, from_capacity, nullptr, 0U);
  }

  /// Destroys the object at the given position without invalidating the
  /// vtable
  void
  weak_destroy(data_accessor* from,
               std::size_t from_capacity) noexcept(HasStrongExceptGuarantee) {
    cmd_(this, opcode::op_weak_destroy, from, from_capacity, nullptr, 0U);
  }

  /// Returns true when the vtable doesn't hold any erased object
  bool empty() const noexcept {
    data_accessor data;
    cmd_(nullptr, opcode::op_fetch_empty, nullptr, 0U, &data, 0U);
    return bool(data.inplace_storage_);
  }

  /// Invoke the function at the given index
  template <std::size_t Index, typename... Args>
  constexpr decltype(auto) invoke(Args&&... args) const {
    auto thunk = invoke_table_t::template fetch<Index>(vtable_);
    return thunk(std::forward<Args>(args)...);
  }
  /// Invoke the function at the given index
  template <std::size_t Index, typename... Args>
  constexpr decltype(auto) invoke(Args&&... args) const volatile {
    auto thunk = invoke_table_t::template fetch<Index>(vtable_);
    return thunk(std::forward<Args>(args)...);
  }

  template <typename T>
  void set_inplace() noexcept {
    using type = std::decay_t<T>;
    vtable_ = invoke_table_t::template get_invocation_table_of<type, true>();
    cmd_ = &trait<type>::template process_cmd<true>;
  }

  template <typename T>
  void set_allocated() noexcept {
    using type = std::decay_t<T>;
    vtable_ = invoke_table_t::template get_invocation_table_of<type, false>();
    cmd_ = &trait<type>::template process_cmd<false>;
  }

  void set_empty() noexcept {
    vtable_ = invoke_table_t::template get_empty_invocation_table<IsThrowing>();
    cmd_ = &empty_cmd;
  }
};
// namespace tables

/// A union which makes the pointer to the heap object share the
/// same space with the internal capacity.
/// The storage type is distinguished by multiple versions of the
/// control and vtable.
template <typename Capacity, typename = void>
struct internal_capacity {
  /// We extend the union through a technique similar to the tail object hack
  typedef union {
    /// Tag to access the structure in a type-safe way
    data_accessor accessor_;
    /// The internal capacity we use to allocate in-place
    std::aligned_storage_t<Capacity::capacity, Capacity::alignment> capacity_;
  } type;
};
template <typename Capacity>
struct internal_capacity<
    Capacity, std::enable_if_t<(Capacity::capacity < sizeof(void*))>> {
  typedef struct {
    /// Tag to access the structure in a type-safe way
    data_accessor accessor_;
  } type;
};

template <typename Capacity>
class internal_capacity_holder {
  // Tag to access the structure in a type-safe way
  typename internal_capacity<Capacity>::type storage_;

public:
  constexpr internal_capacity_holder() = default;

  FU2_DETAIL_CXX14_CONSTEXPR data_accessor* opaque_ptr() noexcept {
    return &storage_.accessor_;
  }
  constexpr data_accessor const* opaque_ptr() const noexcept {
    return &storage_.accessor_;
  }
  FU2_DETAIL_CXX14_CONSTEXPR data_accessor volatile*
  opaque_ptr() volatile noexcept {
    return &storage_.accessor_;
  }
  constexpr data_accessor const volatile* opaque_ptr() const volatile noexcept {
    return &storage_.accessor_;
  }

  static constexpr std::size_t capacity() noexcept {
    return sizeof(storage_);
  }
};

/// An owning erasure
template <bool IsOwning /* = true*/, typename Config, typename Property>
class erasure : internal_capacity_holder<typename Config::capacity> {
  template <booltypenametypename>
  friend class erasure;
  template <std::size_t, typenametypename...>
  friend class operator_impl;

  using vtable_t = tables::vtable<Property>;

  vtable_t vtable_;

public:
  /// Returns the capacity of this erasure
  static constexpr std::size_t capacity() noexcept {
    return internal_capacity_holder<typename Config::capacity>::capacity();
  }

  FU2_DETAIL_CXX14_CONSTEXPR erasure() noexcept {
    vtable_.set_empty();
  }

  FU2_DETAIL_CXX14_CONSTEXPR erasure(std::nullptr_t) noexcept {
    vtable_.set_empty();
  }

  FU2_DETAIL_CXX14_CONSTEXPR
  erasure(erasure&& right) noexcept(Property::is_strong_exception_guaranteed) {
    right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(),
                       this->opaque_ptr(), capacity());
  }

  FU2_DETAIL_CXX14_CONSTEXPR erasure(erasure const& right) {
    right.vtable_.copy(vtable_, right.opaque_ptr(), right.capacity(),
                       this->opaque_ptr(), capacity());
  }

  template <typename OtherConfig>
  FU2_DETAIL_CXX14_CONSTEXPR
  erasure(erasure<true, OtherConfig, Property> right) noexcept(
      Property::is_strong_exception_guaranteed) {
    right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(),
                       this->opaque_ptr(), capacity());
  }

  template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
  FU2_DETAIL_CXX14_CONSTEXPR erasure(std::false_type /*use_bool_op*/,
                                     T&& callable,
                                     Allocator&& allocator_ = Allocator{}) {
    vtable_t::init(vtable_,
                   type_erasure::make_box(
                       std::integral_constant<bool, Config::is_copyable>{},
                       std::forward<T>(callable),
                       std::forward<Allocator>(allocator_)),
                   this->opaque_ptr(), capacity());
  }
  template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
  FU2_DETAIL_CXX14_CONSTEXPR erasure(std::true_type /*use_bool_op*/,
                                     T&& callable,
                                     Allocator&& allocator_ = Allocator{}) {
    if (!!callable) {
      vtable_t::init(vtable_,
                     type_erasure::make_box(
                         std::integral_constant<bool, Config::is_copyable>{},
                         std::forward<T>(callable),
                         std::forward<Allocator>(allocator_)),
                     this->opaque_ptr(), capacity());
    } else {
      vtable_.set_empty();
    }
  }

  ~erasure() {
    vtable_.weak_destroy(this->opaque_ptr(), capacity());
  }

  FU2_DETAIL_CXX14_CONSTEXPR erasure&
  operator=(std::nullptr_t) noexcept(Property::is_strong_exception_guaranteed) {
    vtable_.destroy(this->opaque_ptr(), capacity());
    return *this;
  }

  FU2_DETAIL_CXX14_CONSTEXPR erasure& operator=(erasure&& right) noexcept(
      Property::is_strong_exception_guaranteed) {
    vtable_.weak_destroy(this->opaque_ptr(), capacity());
    right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(),
                       this->opaque_ptr(), capacity());
    return *this;
  }

  FU2_DETAIL_CXX14_CONSTEXPR erasure& operator=(erasure const& right) {
    vtable_.weak_destroy(this->opaque_ptr(), capacity());
    right.vtable_.copy(vtable_, right.opaque_ptr(), right.capacity(),
                       this->opaque_ptr(), capacity());
    return *this;
  }

  template <typename OtherConfig>
  FU2_DETAIL_CXX14_CONSTEXPR erasure&
  operator=(erasure<true, OtherConfig, Property> right) noexcept(
      Property::is_strong_exception_guaranteed) {
    vtable_.weak_destroy(this->opaque_ptr(), capacity());
    right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(),
                       this->opaque_ptr(), capacity());
    return *this;
  }

  template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
  void assign(std::false_type /*use_bool_op*/, T&& callable,
              Allocator&& allocator_ = {}) {
    vtable_.weak_destroy(this->opaque_ptr(), capacity());
    vtable_t::init(vtable_,
                   type_erasure::make_box(
                       std::integral_constant<bool, Config::is_copyable>{},
                       std::forward<T>(callable),
                       std::forward<Allocator>(allocator_)),
                   this->opaque_ptr(), capacity());
  }

  template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
  void assign(std::true_type /*use_bool_op*/, T&& callable,
              Allocator&& allocator_ = {}) {
    if (!!callable) {
      assign(std::false_type{}, std::forward<T>(callable),
             std::forward<Allocator>(allocator_));
    } else {
      operator=(nullptr);
    }
  }

  /// Returns true when the erasure doesn't hold any erased object
  constexpr bool empty() const noexcept {
    return vtable_.empty();
  }

  /// Invoke the function of the erasure at the given index
  ///
  /// We define this out of class to be able to forward the qualified
  /// erasure correctly.
  template <std::size_t Index, typename Erasure, typename... Args>
  static constexpr decltype(auto) invoke(Erasure&& erasure, Args&&... args) {
    auto const capacity = erasure.capacity();
    return erasure.vtable_.template invoke<Index>(
        std::forward<Erasure>(erasure).opaque_ptr(), capacity,
        std::forward<Args>(args)...);
  }
};

// A non owning erasure
template </*bool IsOwning = false, */ typename Config, bool IsThrowing,
          bool HasStrongExceptGuarantee, typename... Args>
class erasure<false, Config,
              property<IsThrowing, HasStrongExceptGuarantee, Args...>> {
  template <booltypenametypename>
  friend class erasure;
  template <std::size_t, typenametypename...>
  friend class operator_impl;

  using property_t = property<IsThrowing, HasStrongExceptGuarantee, Args...>;

  using invoke_table_t = invocation_table::invoke_table<Args...>;
  typename invoke_table_t::type invoke_table_;

  /// The internal pointer to the non owned object
  data_accessor view_;

public:
  // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
  constexpr erasure() noexcept
      : invoke_table_(
            invoke_table_t::template get_empty_invocation_table<IsThrowing>()),
        view_(nullptr) {
  }

  // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
  constexpr erasure(std::nullptr_t) noexcept
      : invoke_table_(
            invoke_table_t::template get_empty_invocation_table<IsThrowing>()),
        view_(nullptr) {
  }

  // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
  constexpr erasure(erasure&& right) noexcept
      : invoke_table_(right.invoke_table_), view_(right.view_) {
  }

  constexpr erasure(erasure const/*right*/) = default;

  template <typename OtherConfig>
  // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
  constexpr erasure(erasure<false, OtherConfig, property_t> right) noexcept
      : invoke_table_(right.invoke_table_), view_(right.view_) {
  }

  template <typename T>
  // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
  constexpr erasure(std::false_type /*use_bool_op*/, T&& object)
      : invoke_table_(invoke_table_t::template get_invocation_view_table_of<
                      std::decay_t<T>>()),
        view_(address_taker<std::decay_t<T>>::take(std::forward<T>(object))) {
  }
  template <typename T>
  // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
  FU2_DETAIL_CXX14_CONSTEXPR erasure(std::true_type use_bool_op, T&& object) {
    this->assign(use_bool_op, std::forward<T>(object));
  }

  ~erasure() = default;

  constexpr erasure&
  operator=(std::nullptr_t) noexcept(HasStrongExceptGuarantee) {
    invoke_table_ =
        invoke_table_t::template get_empty_invocation_table<IsThrowing>();
    view_.ptr_ = nullptr;
    return *this;
  }

  constexpr erasure& operator=(erasure&& right) noexcept {
    invoke_table_ = right.invoke_table_;
    view_ = right.view_;
    right = nullptr;
    return *this;
  }

  constexpr erasure& operator=(erasure const/*right*/) = default;

  template <typename OtherConfig>
  constexpr erasure&
  operator=(erasure<true, OtherConfig, property_t> right) noexcept {
    invoke_table_ = right.invoke_table_;
    view_ = right.view_;
    return *this;
  }

  template <typename T>
  constexpr void assign(std::false_type /*use_bool_op*/, T&& callable) {
    invoke_table_ = invoke_table_t::template get_invocation_view_table_of<
        std::decay_t<T>>();
    view_.assign_ptr(
        address_taker<std::decay_t<T>>::take(std::forward<T>(callable)));
  }
  template <typename T>
  constexpr void assign(std::true_type /*use_bool_op*/, T&& callable) {
    if (!!callable) {
      assign(std::false_type{}, std::forward<T>(callable));
    } else {
      operator=(nullptr);
    }
  }

  /// Returns true when the erasure doesn't hold any erased object
  constexpr bool empty() const noexcept {
    return view_.ptr_ == nullptr;
  }

  template <std::size_t Index, typename Erasure, typename... T>
  static constexpr decltype(auto) invoke(Erasure&& erasure, T&&... args) {
    auto thunk = invoke_table_t::template fetch<Index>(erasure.invoke_table_);
    return thunk(&(erasure.view_), 0UL, std::forward<T>(args)...);
  }
};
// namespace type_erasure

/// Deduces to a true_type if the type T provides the given signature and the
/// signature is noexcept correct callable.
template <typename T, typename Signature,
          typename Trait =
              type_erasure::invocation_table::function_trait<Signature>>
struct accepts_one
    : detail::lazy_and< // both are std::integral_constant
          invocation::can_invoke<typename Trait::template callable<T>,
                                 typename Trait::arguments>,
          invocation::is_noexcept_correct<Trait::is_noexcept::value,
                                          typename Trait::template callable<T>,
                                          typename Trait::arguments>> {};

/// Deduces to a true_type if the type T provides all signatures
template <typename T, typename Signatures, typename = void>
struct accepts_all : std::false_type {};
template <typename T, typename... Signatures>
struct accepts_all<
    T, identity<Signatures...>,
    void_t<std::enable_if_t<accepts_one<T, Signatures>::value>...>>
    : std::true_type {};

#if defined(FU2_HAS_NO_EMPTY_PROPAGATION)
template <typename T>
struct use_bool_op : std::false_type {};
#elif defined(FU2_HAS_LIMITED_EMPTY_PROPAGATION)
/// Implementation for use_bool_op based on the behaviour of std::function,
/// propagating empty state for pointers, `std::function` and
/// `fu2::detail::function` types only.
template <typename T>
struct use_bool_op : std::false_type {};

#if !defined(FU2_HAS_NO_FUNCTIONAL_HEADER)
template <typename Signature>
struct use_bool_op<std::function<Signature>> : std::true_type {};
#endif

template <typename Config, typename Property>
struct use_bool_op<function<Config, Property>> : std::true_type {};

template <typename T>
struct use_bool_op<T*> : std::true_type {};

template <typename Classtypename T>
struct use_bool_op<T Class::*> : std::true_type {};
#else
template <typename T, typename = void>
struct has_bool_op : std::false_type {};
template <typename T>
struct has_bool_op<T, void_t<decltype(bool(std::declval<T>()))>>
    : std::true_type {
#ifndef NDEBUG
  static_assert(!std::is_pointer<T>::value,
                "Missing deduction for function pointer!");
#endif
};

/// Deduces to a true_type if the type T is implementing operator bool()
/// or if the type is convertible to bool directly, this also implements an
/// optimizations for function references `void(&)()` which are can never
/// be null and for such a conversion to bool would never return false.
template <typename T>
struct use_bool_op : has_bool_op<T> {};

#define FU2_DEFINE_USE_OP_TRAIT(CONSTVOLATILE, NOEXCEPT)                     \
  template <typename Ret, typename... Args>                                    \
  struct use_bool_op<Ret (*CONST VOLATILE)(Args...) NOEXCEPT>                  \
      : std::true_type {};

FU2_DETAIL_EXPAND_CV(FU2_DEFINE_USE_OP_TRAIT)
#undef FU2_DEFINE_USE_OP_TRAIT

template <typename Ret, typename... Args>
struct use_bool_op<Ret(Args...)> : std::false_type {};

#if defined(FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE)
template <typename Ret, typename... Args>
struct use_bool_op<Ret(Args...) noexcept> : std::false_type {};
#endif
#endif // FU2_HAS_NO_EMPTY_PROPAGATION

template <typename Config, typename T>
struct assert_wrong_copy_assign {
  static_assert(!Config::is_owning || !Config::is_copyable ||
                    std::is_copy_constructible<std::decay_t<T>>::value,
                "Can't wrap a non copyable object into a unique function!");

  using type = void;
};

template <bool IsStrongExceptGuaranteed, typename T>
struct assert_no_strong_except_guarantee {
  static_assert(
      !IsStrongExceptGuaranteed ||
          (std::is_nothrow_move_constructible<T>::value &&
           std::is_nothrow_destructible<T>::value),
      "Can't wrap a object an object that has no strong exception guarantees "
      "if this is required by the wrapper!");

  using type = void;
};

/// SFINAES out if the given callable is not copyable correct to the left one.
template <typename LeftConfig, typename RightConfig>
using enable_if_copyable_correct_t =
    std::enable_if_t<(!LeftConfig::is_copyable || RightConfig::is_copyable)>;

template <typename LeftConfig, typename RightConfig>
using is_owning_correct =
    std::integral_constant<bool,
                           (LeftConfig::is_owning == RightConfig::is_owning)>;

/// SFINAES out if the given function2 is not owning correct to this one
template <typename LeftConfig, typename RightConfig>
using enable_if_owning_correct_t =
    std::enable_if_t<is_owning_correct<LeftConfig, RightConfig>::value>;

template <typename Config, bool IsThrowing, bool HasStrongExceptGuarantee,
          typename... Args>
class function<Config, property<IsThrowing, HasStrongExceptGuarantee, Args...>>
    : type_erasure::invocation_table::operator_impl<
          0U,
          function<Config,
                   property<IsThrowing, HasStrongExceptGuarantee, Args...>>,
          Args...> {

  template <typenametypename>
  friend class function;

  template <std::size_t, typenametypename...>
  friend class type_erasure::invocation_table::operator_impl;

  using property_t = property<IsThrowing, HasStrongExceptGuarantee, Args...>;
  using erasure_t =
      type_erasure::erasure<Config::is_owning, Config, property_t>;

  template <typename T>
  using enable_if_can_accept_all_t =
      std::enable_if_t<accepts_all<std::decay_t<T>, identity<Args...>>::value>;

  template <typename Function, typename = void>
  struct is_convertible_to_this : std::false_type {};
  template <typename RightConfig>
  struct is_convertible_to_this<
      function<RightConfig, property_t>,
      void_t<enable_if_copyable_correct_t<Config, RightConfig>,
             enable_if_owning_correct_t<Config, RightConfig>>>
      : std::true_type {};

  template <typename T>
  using enable_if_not_convertible_to_this =
      std::enable_if_t<!is_convertible_to_this<std::decay_t<T>>::value>;

  template <typename T>
  using enable_if_owning_t =
      std::enable_if_t<std::is_same<T, T>::value && Config::is_owning>;

  template <typename T>
  using assert_wrong_copy_assign_t =
      typename assert_wrong_copy_assign<Config, std::decay_t<T>>::type;

  template <typename T>
  using assert_no_strong_except_guarantee_t =
      typename assert_no_strong_except_guarantee<HasStrongExceptGuarantee,
                                                 std::decay_t<T>>::type;

  erasure_t erasure_;

public:
  /// Default constructor which empty constructs the function
  function() = default;
  ~function() = default;

  explicit FU2_DETAIL_CXX14_CONSTEXPR
  function(function const/*right*/) = default;
  explicit FU2_DETAIL_CXX14_CONSTEXPR function(function&& /*right*/) = default;

  /// Copy construction from another copyable function
  template <typename RightConfig,
            std::enable_if_t<RightConfig::is_copyable>* = nullptr,
            enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
            enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
  FU2_DETAIL_CXX14_CONSTEXPR
  function(function<RightConfig, property_t> const& right)
      : erasure_(right.erasure_) {
  }

  /// Move construction from another function
  template <typename RightConfig,
            enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
            enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
  FU2_DETAIL_CXX14_CONSTEXPR function(function<RightConfig, property_t>&& right)
      : erasure_(std::move(right.erasure_)) {
  }

  /// Construction from a callable object which overloads the `()` operator
  template <typename T, //
            enable_if_not_convertible_to_this<T>* = nullptr,
            enable_if_can_accept_all_t<T>* = nullptr,
            assert_wrong_copy_assign_t<T>* = nullptr,
            assert_no_strong_except_guarantee_t<T>* = nullptr>
  FU2_DETAIL_CXX14_CONSTEXPR function(T&& callable)
      : erasure_(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable)) {
  }
  template <typename T, typename Allocator, //
            enable_if_not_convertible_to_this<T>* = nullptr,
            enable_if_can_accept_all_t<T>* = nullptr,
            enable_if_owning_t<T>* = nullptr,
            assert_wrong_copy_assign_t<T>* = nullptr,
            assert_no_strong_except_guarantee_t<T>* = nullptr>
  FU2_DETAIL_CXX14_CONSTEXPR function(T&& callable, Allocator&& allocator_)
      : erasure_(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable),
                 std::forward<Allocator>(allocator_)) {
  }

  /// Empty constructs the function
  FU2_DETAIL_CXX14_CONSTEXPR function(std::nullptr_t np) : erasure_(np) {
  }

  function& operator=(function const/*right*/) = default;
  function& operator=(function&& /*right*/) = default;

  /// Copy assigning from another copyable function
  template <typename RightConfig,
            std::enable_if_t<RightConfig::is_copyable>* = nullptr,
            enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
            enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
  function& operator=(function<RightConfig, property_t> const& right) {
    erasure_ = right.erasure_;
    return *this;
  }

  /// Move assigning from another function
  template <typename RightConfig,
            enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
            enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
  function& operator=(function<RightConfig, property_t>&& right) {
    erasure_ = std::move(right.erasure_);
    return *this;
  }

  /// Move assigning from a callable object
  template <typename T, // ...
            enable_if_not_convertible_to_this<T>* = nullptr,
            enable_if_can_accept_all_t<T>* = nullptr,
            assert_wrong_copy_assign_t<T>* = nullptr,
            assert_no_strong_except_guarantee_t<T>* = nullptr>
  function& operator=(T&& callable) {
    erasure_.assign(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable));
    return *this;
  }

  /// Clears the function
  function& operator=(std::nullptr_t np) {
    erasure_ = np;
    return *this;
  }

  /// Returns true when the function is empty
  bool empty() const noexcept {
--> --------------------

--> maximum size reached

--> --------------------

100%


¤ 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.0.56Bemerkung:  ¤

*Bot Zugriff






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.