#ifndef _ANY_INVOKABLE_H_ #define _ANY_INVOKABLE_H_ #include #include #include // clang-format off /* namespace std { template class any_invocable; // never defined template class any_invocable { public: using result_type = R; // SECTION.3, construct/copy/destroy any_invocable() noexcept; any_invocable(nullptr_t) noexcept; any_invocable(any_invocable&&) noexcept; template any_invocable(F&&); template explicit any_invocable(in_place_type_t, Args&&...); template explicit any_invocable(in_place_type_t, initializer_list, Args&&...); any_invocable& operator=(any_invocable&&) noexcept; any_invocable& operator=(nullptr_t) noexcept; template any_invocable& operator=(F&&); template any_invocable& operator=(reference_wrapper) noexcept; ~any_invocable(); // SECTION.4, any_invocable modifiers void swap(any_invocable&) noexcept; // SECTION.5, any_invocable capacity explicit operator bool() const noexcept; // SECTION.6, any_invocable invocation R operator()(ArgTypes...) cv ref noexcept(noex); // SECTION.7, null pointer comparisons friend bool operator==(const any_invocable&, nullptr_t) noexcept; // SECTION.8, specialized algorithms friend void swap(any_invocable&, any_invocable&) noexcept; }; } */ // clang-format on namespace ofats { namespace any_detail { using buffer = std::aligned_storage_t; template inline constexpr bool is_small_object_v = sizeof(T) <= sizeof(buffer) && alignof(buffer) % alignof(T) == 0 && std::is_nothrow_move_constructible_v; union storage { void* ptr_ = nullptr; buffer buf_; }; enum class action { destroy, move }; template struct handler_traits { template struct handler_base { static void handle(action act, storage* current, storage* other = nullptr) { switch (act) { case (action::destroy): Derived::destroy(*current); break; case (action::move): Derived::move(*current, *other); break; } } }; template struct small_handler : handler_base> { template static void create(storage& s, Args&&... args) { new (static_cast(&s.buf_)) T(std::forward(args)...); } static void destroy(storage& s) noexcept { T& value = *static_cast(static_cast(&s.buf_)); value.~T(); } static void move(storage& dst, storage& src) noexcept { create(dst, std::move(*static_cast(static_cast(&src.buf_)))); destroy(src); } static R call(const storage& s, ArgTypes... args) { return std::invoke( *static_cast(static_cast(&const_cast(s).buf_)), std::forward(args)...); } }; template struct large_handler : handler_base> { template static void create(storage& s, Args&&... args) { s.ptr_ = new T(std::forward(args)...); } static void destroy(storage& s) noexcept { delete static_cast(s.ptr_); } static void move(storage& dst, storage& src) noexcept { dst.ptr_ = src.ptr_; } static R call(const storage& s, ArgTypes... args) { return std::invoke(*static_cast(s.ptr_), std::forward(args)...); } }; template using handler = std::conditional_t, small_handler, large_handler>; }; template struct is_in_place_type : std::false_type {}; template struct is_in_place_type> : std::true_type {}; template inline constexpr auto is_in_place_type_v = is_in_place_type::value; template class any_invocable_impl { template using handler = typename any_detail::handler_traits::template handler; using storage = any_detail::storage; using action = any_detail::action; using handle_func = void (*)(any_detail::action, any_detail::storage*, any_detail::storage*); using call_func = R (*)(const any_detail::storage&, ArgTypes...); public: using result_type = R; any_invocable_impl() noexcept = default; any_invocable_impl(std::nullptr_t) noexcept {} any_invocable_impl(any_invocable_impl&& rhs) noexcept { if (rhs.handle_) { handle_ = rhs.handle_; handle_(action::move, &storage_, &rhs.storage_); call_ = rhs.call_; rhs.handle_ = nullptr; } } any_invocable_impl& operator=(any_invocable_impl&& rhs) noexcept { any_invocable_impl{std::move(rhs)}.swap(*this); return *this; } any_invocable_impl& operator=(std::nullptr_t) noexcept { destroy(); return *this; } ~any_invocable_impl() { destroy(); } void swap(any_invocable_impl& rhs) noexcept { if (handle_) { if (rhs.handle_) { storage tmp; handle_(action::move, &tmp, &storage_); rhs.handle_(action::move, &storage_, &rhs.storage_); handle_(action::move, &rhs.storage_, &tmp); std::swap(handle_, rhs.handle_); std::swap(call_, rhs.call_); } else { rhs.swap(*this); } } else if (rhs.handle_) { rhs.handle_(action::move, &storage_, &rhs.storage_); handle_ = rhs.handle_; call_ = rhs.call_; rhs.handle_ = nullptr; } } explicit operator bool() const noexcept { return handle_ != nullptr; } protected: template void create(Args&&... args) { using hdl = handler; hdl::create(storage_, std::forward(args)...); handle_ = &hdl::handle; call_ = &hdl::call; } void destroy() noexcept { if (handle_) { handle_(action::destroy, &storage_, nullptr); handle_ = nullptr; } } R call(ArgTypes... args) const noexcept(is_noexcept) { return call_(storage_, std::forward(args)...); } friend bool operator==(const any_invocable_impl& f, std::nullptr_t) noexcept { return !f; } friend bool operator==(std::nullptr_t, const any_invocable_impl& f) noexcept { return !f; } friend bool operator!=(const any_invocable_impl& f, std::nullptr_t) noexcept { return static_cast(f); } friend bool operator!=(std::nullptr_t, const any_invocable_impl& f) noexcept { return static_cast(f); } friend void swap(any_invocable_impl& lhs, any_invocable_impl& rhs) noexcept { lhs.swap(rhs); } private: storage storage_; handle_func handle_ = nullptr; call_func call_; }; template using remove_cvref_t = std::remove_cv_t>; template using can_convert = std::conjunction< std::negation, AI>>, std::negation>>, std::is_invocable_r, std::bool_constant<(!noex || std::is_nothrow_invocable_r_v)>, std::is_constructible, F>>; } // namespace any_detail template class any_invocable; #define __OFATS_ANY_INVOCABLE(cv, ref, noex, inv_quals) \ template \ class any_invocable final \ : public any_detail::any_invocable_impl { \ using base_type = any_detail::any_invocable_impl; \ \ public: \ using base_type::base_type; \ \ template < \ class F, \ class = std::enable_if_t::value>> \ any_invocable(F&& f) { \ base_type::template create>(std::forward(f)); \ } \ \ template , \ class = std::enable_if_t< \ std::is_move_constructible_v && \ std::is_constructible_v && \ std::is_invocable_r_v && \ (!noex || std::is_nothrow_invocable_r_v)>> \ explicit any_invocable(std::in_place_type_t, Args&&... args) { \ base_type::template create(std::forward(args)...); \ } \ \ template < \ class T, class U, class... Args, class VT = std::decay_t, \ class = std::enable_if_t< \ std::is_move_constructible_v && \ std::is_constructible_v&, Args...> && \ std::is_invocable_r_v && \ (!noex || \ std::is_nothrow_invocable_r_v)>> \ explicit any_invocable(std::in_place_type_t, \ std::initializer_list il, Args&&... args) { \ base_type::template create(il, std::forward(args)...); \ } \ \ template > \ std::enable_if_t && \ std::is_move_constructible_v, \ any_invocable&> \ operator=(F&& f) { \ any_invocable{std::forward(f)}.swap(*this); \ return *this; \ } \ template \ any_invocable& operator=(std::reference_wrapper f) { \ any_invocable{f}.swap(*this); \ return *this; \ } \ \ R operator()(ArgTypes... args) cv ref noexcept(noex) { \ return base_type::call(std::forward(args)...); \ } \ } // cv -> {`empty`, const} // ref -> {`empty`, &, &&} // noex -> {true, false} // inv_quals -> (is_empty(ref) ? & : ref) __OFATS_ANY_INVOCABLE(, , false, &); // 000 __OFATS_ANY_INVOCABLE(, , true, &); // 001 __OFATS_ANY_INVOCABLE(, &, false, &); // 010 __OFATS_ANY_INVOCABLE(, &, true, &); // 011 __OFATS_ANY_INVOCABLE(, &&, false, &&); // 020 __OFATS_ANY_INVOCABLE(, &&, true, &&); // 021 __OFATS_ANY_INVOCABLE(const, , false, const&); // 100 __OFATS_ANY_INVOCABLE(const, , true, const&); // 101 __OFATS_ANY_INVOCABLE(const, &, false, const&); // 110 __OFATS_ANY_INVOCABLE(const, &, true, const&); // 111 __OFATS_ANY_INVOCABLE(const, &&, false, const&&); // 120 __OFATS_ANY_INVOCABLE(const, &&, true, const&&); // 121 #undef __OFATS_ANY_INVOCABLE } // namespace ofats #endif // _ANY_INVOKABLE_H_