From 26e3edd0afb108c42c6a8ca488ec40c4554fb7e9 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 4 Apr 2024 09:42:01 -0700 Subject: [PATCH] Add initial draft of function_ref --- include/boost/compat/function_ref.hpp | 185 ++++++++++++++++++++++++ include/boost/compat/type_traits.hpp | 3 + test/Jamfile | 6 + test/function_ref_fn_noexcept_test.cpp | 165 +++++++++++++++++++++ test/function_ref_fn_test.cpp | 183 +++++++++++++++++++++++ test/function_ref_obj_noexcept_test.cpp | 181 +++++++++++++++++++++++ test/function_ref_obj_test.cpp | 183 +++++++++++++++++++++++ 7 files changed, 906 insertions(+) create mode 100644 include/boost/compat/function_ref.hpp create mode 100644 test/function_ref_fn_noexcept_test.cpp create mode 100644 test/function_ref_fn_test.cpp create mode 100644 test/function_ref_obj_noexcept_test.cpp create mode 100644 test/function_ref_obj_test.cpp diff --git a/include/boost/compat/function_ref.hpp b/include/boost/compat/function_ref.hpp new file mode 100644 index 0000000..48d7067 --- /dev/null +++ b/include/boost/compat/function_ref.hpp @@ -0,0 +1,185 @@ +#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 +#include + +namespace boost { +namespace compat { + +template +struct function_ref; + +namespace detail { + +template +union thunk_storage { + void* pobj_; + void (*pfn_)() noexcept(NoEx); +}; + +template +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 +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)...); +} + +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 {}; + + template + function_ref_base(fp_tag, F* fn) noexcept : thunk_{}, invoke_(&invoke_function) { + thunk_.pfn_ = reinterpret_cast(fn); + } + + template + function_ref_base(obj_tag, F&& fn) noexcept : thunk_{}, invoke_(&invoke_object) { + thunk_.pobj_ = static_cast(std::addressof(fn)); + } + + 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::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) {} + + 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::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) {} + + 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::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) {} + + 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::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) {} + + 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 diff --git a/include/boost/compat/type_traits.hpp b/include/boost/compat/type_traits.hpp index c4dd1fd..b9a1f10 100644 --- a/include/boost/compat/type_traits.hpp +++ b/include/boost/compat/type_traits.hpp @@ -2,6 +2,7 @@ #define BOOST_COMPAT_TYPE_TRAITS_HPP_INCLUDED // Copyright 2024 Peter Dimov +// Copyright 2024 Christian Mazakas // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -32,6 +33,8 @@ template struct make_void template using void_t = typename detail::make_void::type; +template using add_const_t = typename std::add_const::type; + } // namespace compat } // namespace boost diff --git a/test/Jamfile b/test/Jamfile index 7837868..1a7bd31 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -94,3 +94,9 @@ compile invoke_r_md_constexpr_test.cpp run is_invocable_r_test.cpp ; run is_nothrow_invocable_r_test.cpp ; + +run function_ref_fn_test.cpp ; +run function_ref_obj_test.cpp ; + +run function_ref_fn_noexcept_test.cpp ; +run function_ref_obj_noexcept_test.cpp ; diff --git a/test/function_ref_fn_noexcept_test.cpp b/test/function_ref_fn_noexcept_test.cpp new file mode 100644 index 0000000..c732bfd --- /dev/null +++ b/test/function_ref_fn_noexcept_test.cpp @@ -0,0 +1,165 @@ +// Copyright 2024 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#if !defined(__cpp_noexcept_function_type) + +BOOST_PRAGMA_MESSAGE("Test skipped, __cpp_noexcept_function_type is not defined") +int main() {} + +#else + +#include +#include +#include +#include + +int f0() { return -1; } + +int f1(int x1) noexcept { return x1; } +int g1(int x1) noexcept { return x1 * 2; } + +int f2(int x1, int x2) { return 10 * x1 + x2; } + +int f3(int x1, int x2, int x3) noexcept { return 100 * x1 + 10 * x2 + x3; } + +int g(std::unique_ptr p, std::unique_ptr q) { return 10 * *p + *q; } + +struct X { + int v = 0; + + X() = default; + X(int v_) noexcept : v{v_} {} +}; + +struct Y { + int v = 0; + + Y() = default; + explicit Y(int v_) : v{v_} {} +}; + +struct Z { + int v = 0; + + Z() = default; + Z(int v_) : v{v_} {} +}; + +namespace compat = boost::compat; + +int main() { + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + + struct W { + int w_; + }; + + BOOST_TEST_TRAIT_FALSE((std::is_assignable, W>)); + BOOST_TEST_TRAIT_FALSE((std::is_assignable, W>)); + + BOOST_TEST_TRAIT_FALSE((std::is_assignable, compat::function_ref>)); + BOOST_TEST_TRAIT_FALSE((std::is_assignable, compat::function_ref>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_assignable, compat::function_ref>)); + + BOOST_TEST_TRAIT_FALSE( + (std::is_assignable, compat::function_ref>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_assignable, compat::function_ref>)); + + // f0 + { + BOOST_TEST_TRAIT_FALSE((std::is_constructible, decltype(f0)>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, decltype(f0)>)); + } + + // f1 + { + compat::function_ref fv1(f1); + BOOST_TEST_EQ(fv1(1), 1); + + compat::function_ref fv2(f1); + BOOST_TEST_EQ(fv2(1), 1); + } + + // f2 + { + BOOST_TEST_TRAIT_FALSE((std::is_constructible, decltype(f2)>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, decltype(f2)>)); + } + + // f3 + { + compat::function_ref fv1(f3); + BOOST_TEST_EQ(fv1(1, 2, 3), 123); + + compat::function_ref fv2(f3); + BOOST_TEST_EQ(fv2(1, 2, 3), 123); + } + + // g + { + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, std::unique_ptr) noexcept>, + decltype(g)>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, std::unique_ptr) const noexcept>, + decltype(g)>)); + } + + // invoke_r + { + compat::function_ref fv1(f3); + BOOST_TEST_EQ(fv1(1, 2, 3).v, 123); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, decltype(f3)>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, decltype(f3)>)); + + compat::function_ref fv2(f3); + BOOST_TEST_EQ(fv2(1, 2, 3).v, 123); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, decltype(f3)>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, decltype(f3)>)); + + compat::function_ref fv3(f3); + fv3(1, 2, 3); + + compat::function_ref fv4(f3); + fv4(1, 2, 3); + } + + // copy construct, copy assign + { + compat::function_ref fv(f1); + compat::function_ref fv2(fv); + BOOST_TEST_EQ(fv(42), fv2(42)); + + fv2 = g1; + BOOST_TEST_EQ(fv2(12), 24); + + compat::function_ref cfv(f1); + compat::function_ref cfv2(cfv); + BOOST_TEST_EQ(cfv(42), cfv2(42)); + + cfv2 = g1; + BOOST_TEST_EQ(cfv2(24), 48); + } + + return boost::report_errors(); +} + +#endif diff --git a/test/function_ref_fn_test.cpp b/test/function_ref_fn_test.cpp new file mode 100644 index 0000000..79e8065 --- /dev/null +++ b/test/function_ref_fn_test.cpp @@ -0,0 +1,183 @@ +// Copyright 2024 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#if defined(__GNUC__) && __GNUC__ == 7 +#pragma GCC diagnostic ignored "-Wnoexcept-type" +#endif + +#include + +#include +#include +#include +#include +#include + +int f0() { return -1; } +int g0() { return 7331; } + +int f1(int x1) noexcept { return x1; } + +int f2(int x1, int x2) { return 10 * x1 + x2; } + +int f3(int x1, int x2, int x3) noexcept { return 100 * x1 + 10 * x2 + x3; } + +int g(std::unique_ptr p, std::unique_ptr q) { return 10 * *p + *q; } + +struct X { + int v = 0; + + X() = default; + X(int v_) noexcept : v{v_} {} +}; + +struct Y { + int v = 0; + + Y() = default; + explicit Y(int v_) : v{v_} {} +}; + +struct Z { + int v = 0; + + Z() = default; + Z(int v_) : v{v_} {} +}; + +namespace compat = boost::compat; + +int main() { + +#if !defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS) + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_trivially_copyable, std::unique_ptr)>>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_trivially_copyable, std::unique_ptr) const>>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + BOOST_TEST_TRAIT_TRUE((std::is_trivially_copyable>)); + +#if !BOOST_WORKAROUND(BOOST_MSVC, < 1910) + struct W { + int w_; + }; + + BOOST_TEST_TRAIT_FALSE((std::is_assignable, W>)); + BOOST_TEST_TRAIT_FALSE((std::is_assignable, W>)); + + BOOST_TEST_TRAIT_FALSE((std::is_assignable, compat::function_ref>)); + BOOST_TEST_TRAIT_FALSE((std::is_assignable, compat::function_ref>)); + + BOOST_TEST_TRAIT_FALSE((std::is_assignable, compat::function_ref>)); + BOOST_TEST_TRAIT_FALSE((std::is_assignable, compat::function_ref>)); +#endif + +#else + BOOST_PRAGMA_MESSAGE(" is incomplete, skipping is_trivially_copyable checks") +#endif + + // f0 + { + compat::function_ref fv1(f0); + BOOST_TEST_EQ(fv1(), -1); + + compat::function_ref fv2(f0); + BOOST_TEST_EQ(fv2(), -1); + } + + // f1 + { + compat::function_ref fv1(f1); + BOOST_TEST_EQ(fv1(1), 1); + + compat::function_ref fv2(f1); + BOOST_TEST_EQ(fv2(1), 1); + } + + // f2 + { + compat::function_ref fv1(f2); + BOOST_TEST_EQ(fv1(1, 2), 12); + + compat::function_ref fv2(f2); + BOOST_TEST_EQ(fv2(1, 2), 12); + } + + // f3 + { + compat::function_ref fv1(f3); + BOOST_TEST_EQ(fv1(1, 2, 3), 123); + + compat::function_ref fv2(f3); + BOOST_TEST_EQ(fv2(1, 2, 3), 123); + } + + // g + { + using S1 = int(std::unique_ptr, std::unique_ptr); + using S2 = int(std::unique_ptr, std::unique_ptr) const; + + compat::function_ref fv1(g); + { + auto p = std::unique_ptr(new int{1}); + auto q = std::unique_ptr(new int{2}); + BOOST_TEST_EQ(fv1(std::move(p), std::move(q)), 12); + } + + compat::function_ref fv2(g); + { + auto p = std::unique_ptr(new int{130}); + auto q = std::unique_ptr(new int{37}); + BOOST_TEST_EQ(fv1(std::move(p), std::move(q)), 1337); + } + } + + // invoke_r + { + compat::function_ref fv1(f3); + BOOST_TEST_EQ(fv1(1, 2, 3).v, 123); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, decltype(f3)>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, decltype(f3)>)); + + compat::function_ref fv2(f3); + BOOST_TEST_EQ(fv2(1, 2, 3).v, 123); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, decltype(f3)>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, decltype(f3)>)); + + compat::function_ref fv3(f3); + fv3(1, 2, 3); + + compat::function_ref fv4(f3); + fv4(1, 2, 3); + } + + // copy construct, copy assign + { + compat::function_ref fv(f0); + compat::function_ref fv2(fv); + BOOST_TEST_EQ(fv(), fv2()); + + fv2 = g0; + BOOST_TEST_EQ(fv2(), 7331); + + compat::function_ref cfv(f0); + compat::function_ref cfv2(cfv); + BOOST_TEST_EQ(cfv(), cfv2()); + + cfv2 = g0; + BOOST_TEST_EQ(cfv2(), 7331); + } + + return boost::report_errors(); +} diff --git a/test/function_ref_obj_noexcept_test.cpp b/test/function_ref_obj_noexcept_test.cpp new file mode 100644 index 0000000..ddab3ec --- /dev/null +++ b/test/function_ref_obj_noexcept_test.cpp @@ -0,0 +1,181 @@ +// Copyright 2024 Christian Mazakas +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#if !defined(__cpp_noexcept_function_type) + +BOOST_PRAGMA_MESSAGE("Test skipped, __cpp_noexcept_function_type is not defined") +int main() {} + +#else + +#include +#include +#include +#include + +struct F1 { + int operator()() { return -1; } + + int operator()(int x1) noexcept { return x1; } + + int operator()(int x1, int x2) const { return 10 * x1 + x2; } + + int operator()(int x1, int x2, int x3) const noexcept { return 100 * x1 + 10 * x2 + x3; } +}; + +struct F2 { + int operator()(int x1, int x2) & { return 100 * x1 + 10 * x2 + 1; } + int operator()(int x1, int x2) const& { return 100 * x1 + 10 * x2 + 2; } + int operator()(int x1, int x2) && { return 100 * x1 + 10 * x2 + 3; } + int operator()(int x1, int x2) const&& { return 100 * x1 + 10 * x2 + 4; } +}; + +struct X { + int v = 0; + + X() = default; + X(int v_) noexcept : v{v_} {} +}; + +struct Y { + int v = 0; + + Y() = default; + explicit Y(int v_) : v{v_} {} +}; + +struct Z { + int v = 0; + + Z() = default; + Z(int v_) : v{v_} {} +}; + +namespace compat = boost::compat; + +int main() { + { + F1 f1; + { + using S1 = int() noexcept; + using S2 = int(int) noexcept; + using S3 = int(int, int) noexcept; + using S4 = int(int, int, int) noexcept; + + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1&>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const&>)); + + compat::function_ref fv2(f1); + BOOST_TEST_EQ(fv2(1), 1); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const&>)); + + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1&>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const&>)); + + compat::function_ref fv4(f1); + BOOST_TEST_EQ(fv4(1, 2, 3), 123); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1&>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const&>)); + } + { + using S1 = int() const noexcept; + using S2 = int(int) const noexcept; + using S3 = int(int, int) const noexcept; + using S4 = int(int, int, int) const noexcept; + + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1&>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const&>)); + + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1&>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const&>)); + + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1&>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const&>)); + + compat::function_ref fv4(f1); + BOOST_TEST_EQ(fv4(1, 2, 3), 123); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1&>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const&>)); + } + } + + { + using S1 = int(int, int) noexcept; + using S2 = int(int, int) const noexcept; + + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F2>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F2&>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F2 const>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F2 const&>)); + + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F2>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F2&>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F2 const>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F2 const&>)); + } + + // invoke_r + { + F1 f; + + compat::function_ref fv1(f); + BOOST_TEST_EQ(fv1(1, 2, 3).v, 123); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); + + compat::function_ref fv2(f); + BOOST_TEST_EQ(fv2(1, 2, 3).v, 123); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); + + compat::function_ref fv3(f); + fv3(1, 2, 3); + + compat::function_ref fv4(f); + fv4(1, 2, 3); + } + + // copy construct, copy assign + { + F1 f; + + auto id = [](int x) noexcept { return x + 7331; }; + + compat::function_ref fv(f); + compat::function_ref fv2(fv); + BOOST_TEST_EQ(fv(12), fv2(12)); + + fv2 = compat::function_ref(id); + BOOST_TEST_EQ(fv2(1), 7332); + + auto add = [](int x, int y, int z) noexcept { return x + y + z; }; + compat::function_ref cfv(f); + compat::function_ref cfv2(cfv); + BOOST_TEST_EQ(cfv(1, 2, 3), cfv2(1, 2, 3)); + + cfv2 = compat::function_ref(add); + BOOST_TEST_EQ(cfv2(1, 2, 3), 6); + } + + return boost::report_errors(); +} + +#endif diff --git a/test/function_ref_obj_test.cpp b/test/function_ref_obj_test.cpp new file mode 100644 index 0000000..3136ea8 --- /dev/null +++ b/test/function_ref_obj_test.cpp @@ -0,0 +1,183 @@ +// 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 + +struct F1 { + int operator()() { return -1; } + + int operator()(int x1) noexcept { return x1; } + + int operator()(int x1, int x2) const { return 10 * x1 + x2; } + + int operator()(int x1, int x2, int x3) const noexcept { return 100 * x1 + 10 * x2 + x3; } +}; + +struct F2 { + int operator()(int x1, int x2) & { return 100 * x1 + 10 * x2 + 1; } + int operator()(int x1, int x2) const& { return 100 * x1 + 10 * x2 + 2; } + int operator()(int x1, int x2) && { return 100 * x1 + 10 * x2 + 3; } + int operator()(int x1, int x2) const&& { return 100 * x1 + 10 * x2 + 4; } +}; + +struct X { + int v = 0; + + X() = default; + X(int v_) noexcept : v{v_} {} +}; + +struct Y { + int v = 0; + + Y() = default; + explicit Y(int v_) : v{v_} {} +}; + +struct Z { + int v = 0; + + Z() = default; + Z(int v_) : v{v_} {} +}; + +namespace compat = boost::compat; + +int main() { + { + F1 f; + { + using S1 = int(); + using S2 = int(int); + using S3 = int(int, int); + using S4 = int(int, int, int); + + compat::function_ref fv1(f); + + BOOST_TEST_EQ(fv1(), -1); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const&>)); + + compat::function_ref fv2(f); + + BOOST_TEST_EQ(fv2(1), 1); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const&>)); + + compat::function_ref fv3(f); + + BOOST_TEST_EQ(fv3(1, 2), 12); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1&>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const&>)); + + compat::function_ref fv4(f); + + BOOST_TEST_EQ(fv4(1, 2, 3), 123); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1&>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const&>)); + } + + { + using S1 = int() const; + using S2 = int(int) const; + using S3 = int(int, int) const; + using S4 = int(int, int, int) const; + + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1&>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const&>)); + + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1&>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1 const&>)); + + compat::function_ref fv3(f); + + BOOST_TEST_EQ(fv3(1, 2), 12); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1&>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const&>)); + + compat::function_ref fv4(f); + + BOOST_TEST_EQ(fv4(1, 2, 3), 123); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1&>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const&>)); + } + } + + { + F2 g; + { + compat::function_ref fv1(g); + BOOST_TEST_EQ(fv1(3, 2), 321); + + compat::function_ref fv2(std::move(g)); + BOOST_TEST_EQ(fv2(3, 2), 321); + + compat::function_ref fv3(g); + BOOST_TEST_EQ(fv3(3, 2), 322); + + compat::function_ref fv4(std::move(g)); + BOOST_TEST_EQ(fv4(3, 2), 322); + } + } + + // invoke_r + { + F1 f; + + compat::function_ref fv1(f); + BOOST_TEST_EQ(fv1(1, 2, 3).v, 123); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1>)); + + compat::function_ref fv2(f); + BOOST_TEST_EQ(fv2(1, 2, 3).v, 123); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1>)); + + compat::function_ref fv3(f); + fv3(1, 2, 3); + + compat::function_ref fv4(f); + fv4(1, 2, 3); + } + + // copy construct, copy assign + { + F1 f; + + auto id = [] { return 7331; }; + + compat::function_ref fv(f); + compat::function_ref fv2(fv); + BOOST_TEST_EQ(fv(), fv2()); + + fv2 = compat::function_ref(id); + BOOST_TEST_EQ(fv2(), 7331); + + auto add = [](int x, int y) { return x + y; }; + compat::function_ref cfv(f); + compat::function_ref cfv2(cfv); + BOOST_TEST_EQ(cfv(1, 2), cfv2(1, 2)); + + cfv2 = compat::function_ref(add); + BOOST_TEST_EQ(cfv2(1, 2), 3); + } + + return boost::report_errors(); +}