text: 
  //  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_NO_FUNCTIONAL_HEADER
  #if !defined(FU2_WITH_NO_FUNCTIONAL_HEADER) &&                                 \
      !defined(FU2_NO_FUNCTIONAL_HEADER) &&                                      \
      !defined(FU2_HAS_DISABLED_EXCEPTIONS)
  #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>>;
  
  // 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 void* 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) {
    }
  
    /// 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(const, volatile, noexcept, , &)                                            \
    F(, , noexcept, &, &)                                                        \
    F(const, , noexcept, &, &)                                                   \
    F(, volatile, noexcept, &, &)                                                \
    F(const, volatile, noexcept, &, &)                                           \
    F(, , noexcept, &&, &&)                                                      \
    F(const, , noexcept, &&, &&)                                                 \
    F(, volatile, noexcept, &&, &&)                                              \
    F(const, volatile, noexcept, &&, &&)
  #define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F)                                       \
    F(, , noexcept)                                                              \
    F(const, , noexcept)                                                         \
    F(, volatile, noexcept)                                                      \
    F(const, volatile, 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(const, volatile, , , &)                                                    \
    F(, , , &, &)                                                                \
    F(const, , , &, &)                                                           \
    F(, volatile, , &, &)                                                        \
    F(const, volatile, , &, &)                                                   \
    F(, , , &&, &&)                                                              \
    F(const, , , &&, &&)                                                         \
    F(, volatile, , &&, &&)                                                      \
    F(const, volatile, , &&, &&)                                                 \
    FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F)
  #define FU2_DETAIL_EXPAND_CV(F)                                                \
    F(, , )                                                                      \
    F(const, , )                                                                 \
    F(, volatile, )                                                              \
    F(const, volatile, )                                                         \
    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(CONST, VOLATILE, 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(CONST, VOLATILE, 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, typename, typename...>                              \
      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, typename, typename...>                              \
      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 <bool, typename, typename>
    friend class erasure;
    template <std::size_t, typename, typename...>
    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 (bool(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 (bool(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 <bool, typename, typename>
    friend class erasure;
    template <std::size_t, typename, typename...>
    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_.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 (bool(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
      : std::integral_constant<
            bool, invocation::can_invoke<typename Trait::template callable<T>,
                                         typename Trait::arguments>::value &&
                      invocation::is_noexcept_correct<
                          Trait::is_noexcept::value,
                          typename Trait::template callable<T>,
                          typename Trait::arguments>::value> {};
  
  /// 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 {};
  
  /// 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.
  #if defined(FU2_HAS_NO_EMPTY_PROPAGATION)
  template <typename T>
  struct use_bool_op : std::false_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
  };
  
  template <typename T>
  struct use_bool_op : has_bool_op<T> {};
  
  #define FU2_DEFINE_USE_OP_TRAIT(CONST, VOLATILE, 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 <typename, typename>
    friend class function;
  
    template <std::size_t, typename, typename...>
    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 {
      return erasure_.empty();
    }
  
    /// Returns true when the function isn't empty
    explicit operator bool() const noexcept {
      return !empty();
    }
  
    /// Assigns a new target with an optional allocator
    template <typename T, typename Allocator = std::allocator<std::decay_t<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>
    void assign(T&& callable, Allocator&& allocator = Allocator{}) {
      erasure_.assign(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable),
                      std::forward<Allocator>(allocator));
    }
  
    /// Swaps this function with the given function
    void swap(function& other) noexcept(HasStrongExceptGuarantee) {
      if (&other == this) {
        return;
      }
  
      function cache = std::move(other);
      other = std::move(*this);
      *this = std::move(cache);
    }
  
    /// Swaps the left function with the right one
    friend void swap(function& left,
                     function& right) noexcept(HasStrongExceptGuarantee) {
      left.swap(right);
    }
  
    /// Calls the wrapped callable object
    using type_erasure::invocation_table::operator_impl<
        0U, function<Config, property_t>, Args...>::operator();
  };
  
  template <typename Config, typename Property>
  bool operator==(function<Config, Property> const& f, std::nullptr_t) {
    return !bool(f);
  }
  
  template <typename Config, typename Property>
  bool operator!=(function<Config, Property> const& f, std::nullptr_t) {
    return bool(f);
  }
  
  template <typename Config, typename Property>
  bool operator==(std::nullptr_t, function<Config, Property> const& f) {
    return !bool(f);
  }
  
  template <typename Config, typename Property>
  bool operator!=(std::nullptr_t, function<Config, Property> const& f) {
    return bool(f);
  }
  
  // Default intended object size of the function
  using object_size = std::integral_constant<std::size_t, 32U>;
  } // namespace detail
  } // namespace abi_400
  
  /// Can be passed to function_base as template argument which causes
  /// the internal small buffer to be sized according to the given size,
  /// and aligned with the given alignment.
  template <std::size_t Capacity,
            std::size_t Alignment = alignof(std::max_align_t)>
  struct capacity_fixed {
    static constexpr std::size_t capacity = Capacity;
    static constexpr std::size_t alignment = Alignment;
  };
  
  /// Default capacity for small functor optimization
  struct capacity_default
      : capacity_fixed<detail::object_size::value - (2 * sizeof(void*))> {};
  
  /// Can be passed to function_base as template argument which causes
  /// the internal small buffer to be removed from the callable wrapper.
  /// The owning function_base will then allocate memory for every object
  /// it applies a type erasure on.
  struct capacity_none : capacity_fixed<0UL> {};
  
  /// Can be passed to function_base as template argument which causes
  /// the internal small buffer to be sized such that it can hold
  /// the given object without allocating memory for an applied type erasure.
  template <typename T>
  struct capacity_can_hold {
    static constexpr std::size_t capacity = sizeof(T);
    static constexpr std::size_t alignment = alignof(T);
  };
  
  /// An adaptable function wrapper base for arbitrary functional types.
  ///
  /// \tparam IsOwning Is true when the type erasure shall be owning the object.
  ///
  /// \tparam IsCopyable Defines whether the function is copyable or not
  ///
  /// \tparam Capacity Defines the internal capacity of the function
  ///                  for small functor optimization.
  ///                  The size of the whole function object will be the capacity
  ///                  plus the size of two pointers. If the capacity is zero,
  ///                  the size will increase through one additional pointer
  ///                  so the whole object has the size of 3 * sizeof(void*).
  ///                  The type which is passed to the Capacity template parameter
  ///                  shall provide a capacity and alignment member which
  ///                  looks like the following example:
  /// ```cpp
  /// struct my_capacity {
  ///   static constexpr std::size_t capacity = sizeof(my_type);
  ///   static constexpr std::size_t alignment = alignof(my_type);
  /// };
  /// ```
  ///
  /// \tparam IsThrowing Defines whether the function throws an exception on
  ///                    empty function call, `std::abort` is called otherwise.
  ///
  /// \tparam HasStrongExceptGuarantee Defines whether all objects satisfy the
  ///                                  strong exception guarantees,
  ///                                  which means the function type will satisfy
  ///                                  the strong exception guarantees too.
  ///
  /// \tparam Signatures Defines the signature of the callable wrapper
  ///
  template <bool IsOwning, bool IsCopyable, typename Capacity, bool IsThrowing,
            bool HasStrongExceptGuarantee, typename... Signatures>
  using function_base = detail::function<
      detail::config<IsOwning, IsCopyable, Capacity>,
      detail::property<IsThrowing, HasStrongExceptGuarantee, Signatures...>>;
  
  /// An owning copyable function wrapper for arbitrary callable types.
  template <typename... Signatures>
  using function = function_base<true, true, capacity_default, //
                                 true, false, Signatures...>;
  
  /// An owning non copyable function wrapper for arbitrary callable types.
  template <typename... Signatures>
  using unique_function = function_base<true, false, capacity_default, //
                                        true, false, Signatures...>;
  
  /// A non owning copyable function wrapper for arbitrary callable types.
  template <typename... Signatures>
  using function_view = function_base<false, true, capacity_default, //
                                      true, false, Signatures...>;
  
  #if !defined(FU2_HAS_DISABLED_EXCEPTIONS)
  /// Exception type that is thrown when invoking empty function objects
  /// and exception support isn't disabled.
  ///
  /// Exception support is enabled if
  /// the template parameter 'Throwing' is set to true (default).
  ///
  /// This type will default to std::bad_function_call if the
  /// functional header is used, otherwise the library provides its own type.
  ///
  /// You may disable the inclusion of the functional header
  /// through defining `FU2_WITH_NO_FUNCTIONAL_HEADER`.
  ///
  using detail::type_erasure::invocation_table::bad_function_call;
  #endif
  
  /// Returns a callable object, which unifies all callable objects
  /// that were passed to this function.
  ///
  ///   ```cpp
  ///   auto overloaded = fu2::overload([](std::true_type) { return true; },
  ///                                   [](std::false_type) { return false; });
  ///   ```
  ///
  /// \param  callables A pack of callable objects with arbitrary signatures.
  ///
  /// \returns          A callable object which exposes the
  ///
  template <typename... T>
  constexpr auto overload(T&&... callables) {
    return detail::overloading::overload(std::forward<T>(callables)...);
  }
  } // namespace fu2
  
  #undef FU2_DETAIL_EXPAND_QUALIFIERS
  #undef FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT
  #undef FU2_DETAIL_EXPAND_CV
  #undef FU2_DETAIL_EXPAND_CV_NOEXCEPT
  #undef FU2_DETAIL_UNREACHABLE_INTRINSIC
  #undef FU2_DETAIL_TRAP
  #undef FU2_DETAIL_CXX14_CONSTEXPR
  
  #endif // FU2_INCLUDED_FUNCTION2_HPP_
  