#ifndef BOOST_COMPAT_FUNCTION_REF_HPP_INCLUDED #define BOOST_COMPAT_FUNCTION_REF_HPP_INCLUDED // Copyright 2024 Christian Mazakas. // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt #include #include #include #include #if defined(__cpp_nontype_template_parameter_auto) && __cpp_nontype_template_parameter_auto >= 201606L #define BOOST_COMPAT_HAS_AUTO_NTTP #endif namespace boost { namespace compat { template struct function_ref; #if defined(BOOST_COMPAT_HAS_AUTO_NTTP) template struct nontype_t { explicit nontype_t() = default; }; #endif namespace detail { template union thunk_storage { void* pobj_; void (*pfn_)() noexcept(NoEx); }; template struct invoke_function_holder { static R invoke_function(thunk_storage s, Args&&... args) noexcept(NoEx) { auto f = reinterpret_cast(s.pfn_); return compat::invoke_r(f, std::forward(args)...); } }; template struct invoke_object_holder { static R invoke_object(thunk_storage s, Args&&... args) noexcept(NoEx) { using T = remove_reference_t; using cv_T = conditional_t, T>; return compat::invoke_r(*static_cast(s.pobj_), std::forward(args)...); } }; #if defined(BOOST_COMPAT_HAS_AUTO_NTTP) template struct invoke_mem_fn_holder { static R invoke_mem_fn(thunk_storage /* s */, Args&&... args) noexcept(NoEx) { return compat::invoke_r(f, std::forward(args)...); } }; template struct invoke_target_mem_fn_holder { static R invoke_mem_fn(thunk_storage s, Args&&... args) noexcept(NoEx) { using T = remove_reference_t; using cv_T = conditional_t, T>; return compat::invoke_r(f, *static_cast(s.pobj_), std::forward(args)...); } }; template struct invoke_ptr_mem_fn_holder { static R invoke_mem_fn(thunk_storage s, Args&&... args) noexcept(NoEx) { using cv_T = conditional_t, T>; return compat::invoke_r(f, static_cast(s.pobj_), std::forward(args)...); } }; #endif template struct function_ref_base { private: thunk_storage thunk_ = nullptr; R (*invoke_)(thunk_storage, Args&&...) noexcept(NoEx) = nullptr; public: struct fp_tag {}; struct obj_tag {}; struct mem_fn_tag {}; template function_ref_base(fp_tag, F* fn) noexcept : thunk_{}, invoke_(&invoke_function_holder::invoke_function) { thunk_.pfn_ = reinterpret_cast(fn); } template function_ref_base(obj_tag, F&& fn) noexcept : thunk_{}, invoke_(&invoke_object_holder::invoke_object) { thunk_.pobj_ = const_cast(static_cast(std::addressof(fn))); } #if defined(BOOST_COMPAT_HAS_AUTO_NTTP) template function_ref_base(mem_fn_tag, nontype_t) : thunk_{}, invoke_(&invoke_mem_fn_holder::invoke_mem_fn) { thunk_.pobj_ = nullptr; } template function_ref_base(mem_fn_tag, nontype_t, U&& obj) : thunk_{}, invoke_(&invoke_target_mem_fn_holder::invoke_mem_fn) { thunk_.pobj_ = const_cast(static_cast(std::addressof(obj))); } template function_ref_base(mem_fn_tag, nontype_t, T* obj) : thunk_{}, invoke_(&invoke_ptr_mem_fn_holder::invoke_mem_fn) { thunk_.pobj_ = const_cast(static_cast(obj)); } #endif function_ref_base(const function_ref_base&) noexcept = default; function_ref_base& operator=(const function_ref_base&) noexcept = default; R operator()(Args&&... args) const noexcept(NoEx) { return this->invoke_(thunk_, std::forward(args)...); } }; } // namespace detail template struct function_ref : public detail::function_ref_base { private: using base_type = detail::function_ref_base; using typename base_type::fp_tag; using typename base_type::mem_fn_tag; using typename base_type::obj_tag; template using is_invocable_using = boost::compat::is_invocable_r; public: template ::value && is_invocable_using::value, int> = 0> function_ref(F* fn) noexcept : base_type(fp_tag{}, fn) {} template , enable_if_t, function_ref>::value && !std::is_member_pointer::value && is_invocable_using::value, int> = 0> function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {} #if defined(BOOST_COMPAT_HAS_AUTO_NTTP) template ::value, int> = 0> function_ref(nontype_t x) noexcept : base_type(mem_fn_tag{}, x) {} template , class F = decltype(f), enable_if_t && is_invocable_using::value, int> = 0> function_ref(nontype_t x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward(obj)) {} template ::value, int> = 0> function_ref(nontype_t x, T* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {} #endif function_ref(const function_ref&) noexcept = default; function_ref& operator=(const function_ref&) noexcept = default; template ::value && !std::is_pointer::value, int> = 0> function_ref& operator=(T) = delete; }; template struct function_ref : public detail::function_ref_base { private: using base_type = detail::function_ref_base; using typename base_type::fp_tag; using typename base_type::mem_fn_tag; using typename base_type::obj_tag; template using is_invocable_using = boost::compat::is_invocable_r; public: template ::value && is_invocable_using::value, int> = 0> function_ref(F* fn) noexcept : base_type(fp_tag{}, fn) {} template , enable_if_t, function_ref>::value && !std::is_member_pointer::value && is_invocable_using::value, int> = 0> function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {} #if defined(BOOST_COMPAT_HAS_AUTO_NTTP) template ::value, int> = 0> function_ref(nontype_t x) noexcept : base_type(mem_fn_tag{}, x) {} template , class F = decltype(f), enable_if_t && is_invocable_using::value, int> = 0> function_ref(nontype_t x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward(obj)) {} template ::value, int> = 0> function_ref(nontype_t x, T const* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {} #endif function_ref(const function_ref&) noexcept = default; function_ref& operator=(const function_ref&) noexcept = default; template ::value && !std::is_pointer::value, int> = 0> function_ref& operator=(T) = delete; }; #if defined(__cpp_noexcept_function_type) template struct function_ref : public detail::function_ref_base { private: using base_type = detail::function_ref_base; using typename base_type::fp_tag; using typename base_type::mem_fn_tag; using typename base_type::obj_tag; template using is_invocable_using = boost::compat::is_nothrow_invocable_r; public: template ::value && is_invocable_using::value, int> = 0> function_ref(F* fn) noexcept : base_type(fp_tag{}, fn) {} template , enable_if_t, function_ref>::value && !std::is_member_pointer::value && is_invocable_using::value, int> = 0> function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {} #if defined(BOOST_COMPAT_HAS_AUTO_NTTP) template ::value, int> = 0> function_ref(nontype_t x) noexcept : base_type(mem_fn_tag{}, x) {} template , class F = decltype(f), enable_if_t && is_invocable_using::value, int> = 0> function_ref(nontype_t x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward(obj)) {} template ::value, int> = 0> function_ref(nontype_t x, T* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {} #endif function_ref(const function_ref&) noexcept = default; function_ref& operator=(const function_ref&) noexcept = default; template ::value && !std::is_pointer::value, int> = 0> function_ref& operator=(T) = delete; }; template struct function_ref : public detail::function_ref_base { private: using base_type = detail::function_ref_base; using typename base_type::fp_tag; using typename base_type::mem_fn_tag; using typename base_type::obj_tag; template using is_invocable_using = boost::compat::is_nothrow_invocable_r; public: template ::value && is_invocable_using::value, int> = 0> function_ref(F* fn) noexcept : base_type(fp_tag{}, fn) {} template , enable_if_t, function_ref>::value && !std::is_member_pointer::value && is_invocable_using::value, int> = 0> function_ref(F&& fn) noexcept : base_type(obj_tag{}, fn) {} #if defined(BOOST_COMPAT_HAS_AUTO_NTTP) template ::value, int> = 0> function_ref(nontype_t x) noexcept : base_type(mem_fn_tag{}, x) {} template , class F = decltype(f), enable_if_t && is_invocable_using::value, int> = 0> function_ref(nontype_t x, U&& obj) noexcept : base_type(mem_fn_tag{}, x, std::forward(obj)) {} template ::value, int> = 0> function_ref(nontype_t x, T const* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {} #endif function_ref(const function_ref&) noexcept = default; function_ref& operator=(const function_ref&) noexcept = default; template ::value && !std::is_pointer::value, int> = 0> function_ref& operator=(T) = delete; }; #endif } // namespace compat } // namespace boost #endif // #ifndef BOOST_COMPAT_FUNCTION_REF_HPP_INCLUDED