commit 7f296c7: [Project] Add fu2 library to better functions abstractions

Vsevolod Stakhov vsevolod at highsecure.ru
Mon Mar 29 14:21:15 UTC 2021


Author: Vsevolod Stakhov
Date: 2021-03-29 14:23:09 +0100
URL: https://github.com/rspamd/rspamd/commit/7f296c74ccf9cc73ea141c99c1a6ca7840e2b5d3

[Project] Add fu2 library to better functions abstractions

---
 CMakeLists.txt                              |    1 +
 contrib/DEPENDENCY_INFO.md                  |    1 +
 contrib/{fpconv/LICENSE => fu2/LICENSE.txt} |    0
 contrib/fu2/include/function2/function2.hpp | 1792 +++++++++++++++++++++++++++
 4 files changed, 1794 insertions(+)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a50bba36f..2a42d1886 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -119,6 +119,7 @@ INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/"
 		"${CMAKE_SOURCE_DIR}/contrib/frozen/include"
 		"${CMAKE_SOURCE_DIR}/contrib/fmt/include"
 		"${CMAKE_SOURCE_DIR}/contrib/doctest"
+		"${CMAKE_BINARY_DIR}/contrib/fu2/include"
 		"${CMAKE_BINARY_DIR}/src" #Stored in the binary dir
 		"${CMAKE_BINARY_DIR}/src/libcryptobox")
 
diff --git a/contrib/DEPENDENCY_INFO.md b/contrib/DEPENDENCY_INFO.md
index 9f6a156f9..f64556d33 100644
--- a/contrib/DEPENDENCY_INFO.md
+++ b/contrib/DEPENDENCY_INFO.md
@@ -33,3 +33,4 @@
 | frozen        | 1.0.1   | Apache 2            | NO      |                    |
 | fmt           | 7.1.3   | MIT                 | NO      |                    |
 | doctest       | 2.4.5   | MIT                 | NO      |                    |
+| function2     | 4.1.0   | Boost               | NO      |                    |
\ No newline at end of file
diff --git a/contrib/fpconv/LICENSE b/contrib/fu2/LICENSE.txt
similarity index 100%
copy from contrib/fpconv/LICENSE
copy to contrib/fu2/LICENSE.txt
diff --git a/contrib/fu2/include/function2/function2.hpp b/contrib/fu2/include/function2/function2.hpp
new file mode 100644
index 000000000..a45cb580f
--- /dev/null
+++ b/contrib/fu2/include/function2/function2.hpp
@@ -0,0 +1,1792 @@
+
+//  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*;
+
*** OUTPUT TRUNCATED, 1137 LINES SKIPPED ***


More information about the Commits mailing list