// Copyright 2015-2018 Denis Blank // 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 #include #include #include #include #include // 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) #define FU2_HAS_NO_FUNCTIONAL_HEADER #include #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 #if !defined(FU2_HAS_DISABLED_EXCEPTIONS) #include #endif namespace fu2 { inline namespace abi_310 { namespace detail { template class function; template struct identity {}; // Equivalent to C++17's std::void_t which is targets a bug in GCC, // that prevents correct SFINAE behavior. // See http://stackoverflow.com/questions/35753920 for details. template struct deduce_to_void : std::common_type {}; template using void_t = typename deduce_to_void::type; // Copy enabler helper class template struct copyable {}; template <> struct copyable { 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 struct config { // Is true if the function is copyable. 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. static constexpr auto const capacity = Capacity; }; /// A config which isn't compatible to other configs template 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 = Throws; }; /// Provides utilities for invocing callable objects namespace invocation { /// Invokes the given callable object with the given arguments template constexpr auto invoke(Callable&& callable, Args&&... args) noexcept( noexcept(std::forward(callable)(std::forward(args)...))) -> decltype(std::forward(callable)(std::forward(args)...)) { return std::forward(callable)(std::forward(args)...); } /// Invokes the given member function pointer by reference template constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept( noexcept((std::forward(self).*member)(std::forward(args)...))) -> decltype((std::forward(self).* member)(std::forward(args)...)) { return (std::forward(self).*member)(std::forward(args)...); } /// Invokes the given member function pointer by pointer template constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept( noexcept((std::forward(self)->*member)(std::forward(args)...))) -> decltype( (std::forward(self)->*member)(std::forward(args)...)) { return (std::forward(self)->*member)(std::forward(args)...); } /// Invokes the given pointer to a scalar member by reference template constexpr auto invoke(Type T::*member, Self&& self) noexcept(noexcept(std::forward(self).*member)) -> decltype(std::forward(self).*member) { return (std::forward(self).*member); } /// Invokes the given pointer to a scalar member by pointer template constexpr auto invoke(Type T::*member, Self&& self) noexcept(noexcept(std::forward(self)->*member)) -> decltype(std::forward(self)->*member) { return std::forward(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 struct can_invoke : std::false_type {}; template struct can_invoke, decltype((void)std::declval()(std::declval()...))> : std::true_type {}; template struct can_invoke, decltype((void)((std::declval().*std::declval())( std::declval()...)))> : std::true_type {}; template struct can_invoke, decltype( (void)((std::declval().*std::declval())( std::declval()...)))> : std::true_type {}; template struct can_invoke, decltype( (void)((std::declval()->*std::declval())( std::declval()...)))> : std::true_type {}; template struct can_invoke, decltype((void)(std::declval().*std::declval()))> : std::true_type {}; template struct can_invoke, decltype((void)(std::declval().* std::declval()))> : std::true_type { }; template struct can_invoke, decltype( (void)(std::declval()->*std::declval()))> : std::true_type {}; template struct is_noexcept_correct : std::true_type {}; template struct is_noexcept_correct> : std::integral_constant(), std::declval()...))> { }; } // end namespace invocation namespace overloading { template struct overload_impl; template struct overload_impl : Current, overload_impl { explicit overload_impl(Current current, Next next, Rest... rest) : Current(std::move(current)), overload_impl( std::move(next), std::move(rest)...) { } using Current::operator(); using overload_impl::operator(); }; template struct overload_impl : Current { explicit overload_impl(Current current) : Current(std::move(current)) { } using Current::operator(); }; template constexpr auto overload(T&&... callables) { return overload_impl...>{std::forward(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 struct address_taker { template static void* take(O&& obj) { return std::addressof(obj); } static T& restore(void* ptr) { return *static_cast(ptr); } static T const& restore(void const* ptr) { return *static_cast(ptr); } static T volatile& restore(void volatile* ptr) { return *static_cast(ptr); } static T const volatile& restore(void const volatile* ptr) { return *static_cast(ptr); } }; /// Specialization to work with addresses of raw function pointers template struct address_taker::value>> { template static void* take(O&& obj) { return reinterpret_cast(obj); } template static T restore(O ptr) { return reinterpret_cast(const_cast(ptr)); } }; template struct box_factory; /// Store the allocator inside the box template struct box : private Allocator { friend box_factory; 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 struct box : private Allocator { friend box_factory; 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 struct box_factory> { using real_allocator = typename std::allocator_traits>:: template rebind_alloc>; /// Allocates space through the boxed allocator static box* box_allocate(box const* me) { real_allocator allocator(*static_cast(me)); return static_cast*>( std::allocator_traits::allocate(allocator, 1U)); } /// Destroys the box through the given allocator static void box_deallocate(box* me) { real_allocator allocator(*static_cast(me)); me->~box(); std::allocator_traits::deallocate(allocator, me, 1U); } }; /// Creates a box containing the given value and allocator template >> auto make_box(std::integral_constant, T&& value, Allocator&& allocator = Allocator{}) { return box, std::decay_t>{ std::forward(value), std::forward(allocator)}; } template struct is_box : std::false_type {}; template struct is_box> : 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 constexpr void write_empty(data_accessor* accessor, bool empty) noexcept { accessor->inplace_storage_ = std::size_t(empty); } template using transfer_const_t = std::conditional_t>::value, std::add_const_t, To>; template using transfer_volatile_t = std::conditional_t>::value, std::add_volatile_t, To>; /// The retriever when the object is allocated inplace template constexpr auto retrieve(std::true_type /*is_inplace*/, Accessor from, std::size_t from_capacity) { using type = transfer_const_t>*; /// Process the command by using the data inside the internal capacity auto storage = &(from->inplace_storage_); auto inplace = const_cast(static_cast(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 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"; } }; #elif using std::bad_function_call; #endif #endif #ifdef FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE #define FU2_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, &&, &&) #else // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE #define FU2_EXPAND_QUALIFIERS_NOEXCEPT(F) #endif // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE #define FU2_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_EXPAND_QUALIFIERS_NOEXCEPT(F) /// If the function is qualified as noexcept, the call will never throw template [[noreturn]] void throw_or_abortnoexcept( std::integral_constant /*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 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 \ struct function_trait { \ using pointer_type = Ret (*)(data_accessor CONST VOLATILE*, \ std::size_t capacity, Args...); \ template \ struct internal_invoker { \ static Ret invoke(data_accessor CONST VOLATILE* data, \ std::size_t capacity, Args... args) NOEXCEPT { \ auto obj = retrieve(std::integral_constant{}, \ data, capacity); \ auto box = static_cast(obj); \ return invocation::invoke( \ static_castvalue_)> CONST VOLATILE \ REF>(box->value_), \ std::forward(args)...); \ } \ }; \ \ template \ struct view_invoker { \ static Ret invoke(data_accessor CONST VOLATILE* data, std::size_t, \ Args... args) NOEXCEPT { \ \ auto ptr = static_cast(data->ptr_); \ return invocation::invoke(address_taker::restore(ptr), \ std::forward(args)...); \ } \ }; \ \ template \ using callable = T CONST VOLATILE REF; \ \ using arguments = identity; \ \ using is_noexcept = is_noexcept_##NOEXCEPT; \ \ template \ 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{}); \ } \ }; \ }; FU2_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT) #undef FU2_DEFINE_FUNCTION_TRAIT /// Deduces to the function pointer to the given signature template using function_pointer_of = typename function_trait::pointer_type; template struct invoke_table; /// We optimize the vtable_t in case there is a single function overload template struct invoke_table { using type = function_pointer_of; /// Return the function pointer itself template 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 static constexpr type get_invocation_table_of() noexcept { return &function_trait::template internal_invoker::invoke; } /// Returns the thunk of an single overloaded callable template static constexpr type get_invocation_view_table_of() noexcept { return &function_trait::template view_invoker::invoke; } /// Returns the thunk of an empty single overloaded callable template static constexpr type get_empty_invocation_table() noexcept { return &function_trait::template empty_invoker::invoke; } }; /// We generate a table in case of multiple function overloads template struct invoke_table { using type = std::tuple, function_pointer_of, function_pointer_of...> const*; /// Return the function pointer at the particular index template static constexpr auto fetch(type table) noexcept { return std::get(*table); } /// The invocation vtable for a present object template struct invocation_vtable : public std::tuple, function_pointer_of, function_pointer_of...> { constexpr invocation_vtable() noexcept : std::tuple, function_pointer_of, function_pointer_of...>(std::make_tuple( &function_trait::template internal_invoker< T, IsInplace>::invoke, &function_trait::template internal_invoker< T, IsInplace>::invoke, &function_trait::template internal_invoker< T, IsInplace>::invoke...)) { } }; /// Returns the thunk of an multi overloaded callable template static type get_invocation_table_of() noexcept { static invocation_vtable const table; return &table; } /// The invocation vtable for a present object template struct invocation_view_vtable : public std::tuple, function_pointer_of, function_pointer_of...> { constexpr invocation_view_vtable() noexcept : std::tuple, function_pointer_of, function_pointer_of...>(std::make_tuple( &function_trait::template view_invoker::invoke, &function_trait::template view_invoker::invoke, &function_trait::template view_invoker::invoke...)) { } }; /// Returns the thunk of an multi overloaded callable template static type get_invocation_view_table_of() noexcept { static invocation_view_vtable const table; return &table; } /// The invocation table for an empty wrapper template struct empty_vtable : public std::tuple, function_pointer_of, function_pointer_of...> { constexpr empty_vtable() noexcept : std::tuple, function_pointer_of, function_pointer_of...>( std::make_tuple(&function_trait::template empty_invoker< IsThrowing>::invoke, &function_trait::template empty_invoker< IsThrowing>::invoke, &function_trait::template empty_invoker< IsThrowing>::invoke...)) { } }; /// Returns the thunk of an multi single overloaded callable template static type get_empty_invocation_table() noexcept { static empty_vtable const table; return &table; } }; template class operator_impl; #define FU2_DEFINE_FUNCTION_TRAIT(CONST, VOLATILE, NOEXCEPT, OVL_REF, REF) \ template \ class operator_impl \ : operator_impl { \ \ template \ 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::operator(); \ \ Ret operator()(Args... args) CONST VOLATILE OVL_REF NOEXCEPT { \ auto parent = static_cast(this); \ using erasure_t = std::decay_terasure_)>; \ \ return erasure_t::template invoke( \ static_cast(parent->erasure_), \ std::forward(args)...); \ } \ }; \ template \ class operator_impl, \ Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT> \ : copyable { \ \ template \ 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 CONST VOLATILE*>(this); \ using erasure_t = std::decay_terasure_)>; \ \ return erasure_t::template invoke( \ static_cast(parent->erasure_), \ std::forward(args)...); \ } \ }; FU2_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 class vtable; template class vtable> { 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; command_function_t cmd_; typename invoke_table_t::type vtable_; template struct trait { static_assert(is_box::value, "The trait must be specialized with a box!"); /// The command table template 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(retrieve( std::integral_constant{}, 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(); } // 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(retrieve( std::integral_constant{}, from, from_capacity)); assert(box && "The object must not be over aligned or null!"); assert(std::is_copy_constructible::value && "The box is required to be copyable here!"); // Try to allocate the object inplace construct(std::is_copy_constructible{}, *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(retrieve( std::integral_constant{}, from, from_capacity)); if (IsInplace) { box->~T(); } else { box_factory::box_deallocate(box); } if (op == opcode::op_destroy) { to_table->set_empty(); } return; } case opcode::op_fetch_empty: { write_empty(to, false); return; } } // TODO Use an unreachable intrinsic assert(false && "Unreachable!"); std::exit(-1); } template 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(std::true_type{}, to, to_capacity); if (storage) { to_table->template set_inplace(); } else { // Allocate the object through the allocator to->ptr_ = storage = box_factory>::box_allocate(std::addressof(box)); to_table->template set_allocated(); } new (storage) T(std::forward(box)); } template 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; } } } public: vtable() noexcept = default; /// Initialize an object at the given position template static void init(vtable& table, T&& object, data_accessor* to, std::size_t to_capacity) { trait>::construct(std::true_type{}, std::forward(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(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 constexpr auto invoke(Args&&... args) const { auto thunk = invoke_table_t::template fetch(vtable_); return thunk(std::forward(args)...); } /// Invoke the function at the given index template constexpr auto invoke(Args&&... args) const volatile { auto thunk = invoke_table_t::template fetch(vtable_); return thunk(std::forward(args)...); } template void set_inplace() noexcept { using type = std::decay_t; vtable_ = invoke_table_t::template get_invocation_table_of(); cmd_ = &trait::template process_cmd; } template void set_allocated() noexcept { using type = std::decay_t; vtable_ = invoke_table_t::template get_invocation_table_of(); cmd_ = &trait::template process_cmd; } void set_empty() noexcept { vtable_ = invoke_table_t::template get_empty_invocation_table(); 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 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_; } type; }; template struct internal_capacity> { typedef struct { /// Tag to access the structure in a type-safe way data_accessor accessor_; } type; }; template class internal_capacity_holder { // Tag to access the structure in a type-safe way typename internal_capacity::type storage_; public: constexpr internal_capacity_holder() = default; constexpr data_accessor* opaque_ptr() noexcept { return &storage_.accessor_; } constexpr data_accessor const* opaque_ptr() const noexcept { return &storage_.accessor_; } 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 class erasure : internal_capacity_holder { template friend class erasure; template friend class operator_impl; using vtable_t = tables::vtable; vtable_t vtable_; public: /// Returns the capacity of this erasure static constexpr std::size_t capacity() noexcept { return internal_capacity_holder::capacity(); } constexpr erasure() noexcept { vtable_.set_empty(); } constexpr erasure(std::nullptr_t) noexcept { vtable_.set_empty(); } constexpr erasure(erasure&& right) noexcept( Property::is_strong_exception_guaranteed) { right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), this->opaque_ptr(), capacity()); } constexpr erasure(erasure const& right) { right.vtable_.copy(vtable_, right.opaque_ptr(), right.capacity(), this->opaque_ptr(), capacity()); } template constexpr erasure(erasure right) noexcept( Property::is_strong_exception_guaranteed) { right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(), this->opaque_ptr(), capacity()); } template >> constexpr erasure(T&& callable, Allocator&& allocator = Allocator{}) { vtable_t::init(vtable_, type_erasure::make_box( std::integral_constant{}, std::forward(callable), std::forward(allocator)), this->opaque_ptr(), capacity()); } ~erasure() { vtable_.weak_destroy(this->opaque_ptr(), capacity()); } constexpr erasure& operator=(std::nullptr_t) noexcept(Property::is_strong_exception_guaranteed) { vtable_.destroy(this->opaque_ptr(), capacity()); return *this; } 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; } 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 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; } template constexpr erasure& operator=(T&& callable) { vtable_.weak_destroy(this->opaque_ptr(), capacity()); vtable_t::init(vtable_, type_erasure::make_box( std::integral_constant{}, std::forward(callable)), this->opaque_ptr(), capacity()); return *this; } template void assign(T&& callable, Allocator&& allocator) { vtable_.weak_destroy(this->opaque_ptr(), capacity()); vtable_t::init(vtable_, type_erasure::make_box( std::integral_constant{}, std::forward(callable), std::forward(allocator)), this->opaque_ptr(), capacity()); } /// 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 static constexpr auto invoke(Erasure&& erasure, Args&&... args) { auto const capacity = erasure.capacity(); return erasure.vtable_.template invoke( std::forward(erasure).opaque_ptr(), capacity, std::forward(args)...); } }; // A non owning erasure template class erasure> { template friend class erasure; template friend class operator_impl; using property_t = property; using invoke_table_t = invocation_table::invoke_table; 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()), view_(nullptr) { } // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) constexpr erasure(std::nullptr_t) noexcept : invoke_table_( invoke_table_t::template get_empty_invocation_table()), 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 // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) constexpr erasure(erasure right) noexcept : invoke_table_(right.invoke_table_), view_(right.view_) { } template // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init) constexpr erasure(T&& object) : invoke_table_(invoke_table_t::template get_invocation_view_table_of< std::decay_t>()), view_(address_taker>::take(std::forward(object))) { } ~erasure() = default; constexpr erasure& operator=(std::nullptr_t) noexcept(HasStrongExceptGuarantee) { invoke_table_ = invoke_table_t::template get_empty_invocation_table(); 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 constexpr erasure& operator=(erasure right) noexcept { invoke_table_ = right.invoke_table_; view_ = right.view_; return *this; } template constexpr erasure& operator=(T&& object) { invoke_table_ = invoke_table_t::template get_invocation_view_table_of< std::decay_t>(); view_.ptr_ = address_taker>::take(std::forward(object)); return *this; } /// Returns true when the erasure doesn't hold any erased object constexpr bool empty() const noexcept { return view_.ptr_ == nullptr; } template static constexpr auto invoke(Erasure&& erasure, T&&... args) { auto thunk = invoke_table_t::template fetch(erasure.invoke_table_); return thunk(&(erasure.view_), 0UL, std::forward(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 > struct accepts_one : std::integral_constant< bool, invocation::can_invoke, typename Trait::arguments>::value && invocation::is_noexcept_correct< Trait::is_noexcept::value, typename Trait::template callable, typename Trait::arguments>::value> {}; /// Deduces to a true_type if the type T provides all signatures template struct accepts_all : std::false_type {}; template struct accepts_all< T, identity, void_t::value>...>> : std::true_type {}; template struct assert_wrong_copy_assign { static_assert(!Config::is_copyable || std::is_copy_constructible>::value, "Can't wrap a non copyable object into a unique function!"); using type = void; }; template struct assert_no_strong_except_guarantee { static_assert( !IsStrongExceptGuaranteed || (std::is_nothrow_move_constructible::value && std::is_nothrow_destructible::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 using enable_if_copyable_correct_t = std::enable_if_t<(!LeftConfig::is_copyable || RightConfig::is_copyable)>; template using is_owning_correct = std::integral_constant; /// SFINAES out if the given function2 is not owning correct to this one template using enable_if_owning_correct_t = std::enable_if_t::value>; template class function> : type_erasure::invocation_table::operator_impl< 0U, function>, Args...> { template friend class function; template friend class type_erasure::invocation_table::operator_impl; using property_t = property; using erasure_t = type_erasure::erasure; template using enable_if_can_accept_all_t = std::enable_if_t, identity>::value>; template struct is_convertible_to_this : std::false_type {}; template struct is_convertible_to_this< function, void_t, enable_if_owning_correct_t>> : std::true_type {}; template using enable_if_not_convertible_to_this = std::enable_if_t>::value>; template using enable_if_owning_t = std::enable_if_t::value && Config::is_owning>; template using assert_wrong_copy_assign_t = typename assert_wrong_copy_assign>::type; template using assert_no_strong_except_guarantee_t = typename assert_no_strong_except_guarantee>::type; erasure_t erasure_; public: /// Default constructor which empty constructs the function function() = default; ~function() = default; explicit constexpr function(function const& /*right*/) = default; explicit constexpr function(function&& /*right*/) = default; /// Copy construction from another copyable function template * = nullptr, enable_if_copyable_correct_t* = nullptr, enable_if_owning_correct_t* = nullptr> constexpr function(function const& right) : erasure_(right.erasure_) { } /// Move construction from another function template * = nullptr, enable_if_owning_correct_t* = nullptr> constexpr function(function&& right) : erasure_(std::move(right.erasure_)) { } /// Construction from a callable object which overloads the `()` operator template * = nullptr, enable_if_can_accept_all_t* = nullptr, assert_wrong_copy_assign_t* = nullptr, assert_no_strong_except_guarantee_t* = nullptr> constexpr function(T&& callable) : erasure_(std::forward(callable)) { } template * = nullptr, enable_if_can_accept_all_t* = nullptr, enable_if_owning_t* = nullptr, assert_wrong_copy_assign_t* = nullptr, assert_no_strong_except_guarantee_t* = nullptr> constexpr function(T&& callable, Allocator&& allocator) : erasure_(std::forward(callable), std::forward(allocator)) { } /// Empty constructs the function 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 * = nullptr, enable_if_copyable_correct_t* = nullptr, enable_if_owning_correct_t* = nullptr> function& operator=(function const& right) { erasure_ = right.erasure_; return *this; } /// Move assigning from another function template * = nullptr, enable_if_owning_correct_t* = nullptr> function& operator=(function&& right) { erasure_ = std::move(right.erasure_); return *this; } /// Move assigning from a callable object template * = nullptr, enable_if_can_accept_all_t* = nullptr, assert_wrong_copy_assign_t* = nullptr, assert_no_strong_except_guarantee_t* = nullptr> function& operator=(T&& callable) { erasure_ = std::forward(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 >, enable_if_not_convertible_to_this* = nullptr, enable_if_can_accept_all_t* = nullptr, assert_wrong_copy_assign_t* = nullptr, assert_no_strong_except_guarantee_t* = nullptr> void assign(T&& callable, Allocator&& allocator = Allocator{}) { erasure_.assign(std::forward(callable), std::forward(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, Args...>::operator(); }; template bool operator==(function const& f, std::nullptr_t) { return !bool(f); } template bool operator!=(function const& f, std::nullptr_t) { return bool(f); } template bool operator==(std::nullptr_t, function const& f) { return !bool(f); } template bool operator!=(std::nullptr_t, function const& f) { return bool(f); } // Default object size of the function using object_size = std::integral_constant; // Default capacity for small functor optimization using default_capacity = std::integral_constant; } // namespace detail } // namespace abi_310 /// Adaptable function wrapper base for arbitrary functional types. template < /// This is a placeholder for future non owning support bool IsOwning, /// Defines whether the function is copyable or not bool IsCopyable, /// 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*). std::size_t Capacity, /// Defines whether the function throws an exception on empty function /// call, `std::abort` is called otherwise. bool IsThrowing, /// Defines whether all objects satisfy the strong exception guarantees, /// which means the function type will satisfy the strong exception /// guarantees too. bool HasStrongExceptGuarantee, /// Defines the signature of the function wrapper typename... Signatures> using function_base = detail::function< detail::config, detail::property>; /// An owning copyable function wrapper for arbitrary callable types. template using function = function_base; /// An owning non copyable function wrapper for arbitrary callable types. template using unique_function = function_base; /// A non owning copyable function wrapper for arbitrary callable types. template using function_view = function_base; #if !defined(FU2_HAS_DISABLED_EXCEPTIONS) /// Exception type that is thrown when invoking empty function objects /// and exception support isn't disabled. /// /// Exception suport 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 functionl 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 constexpr auto overload(T&&... callables) { return detail::overloading::overload(std::forward(callables)...); } } // namespace fu2 #undef FU2_EXPAND_QUALIFIERS #undef FU2_EXPAND_QUALIFIERS_NOEXCEPT #endif // FU2_INCLUDED_FUNCTION2_HPP_