From ef0f49933b1cb738a53aaeb327cf2b5fddedf6dd Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Wed, 19 Jun 2024 03:36:09 +0300 Subject: [PATCH 1/6] Update ci.yml --- .github/workflows/ci.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7ffda8..9c4d06d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,8 +54,7 @@ jobs: install: g++-10 - toolset: gcc-11 cxxstd: "03,11,14,17,2a" - os: ubuntu-20.04 - install: g++-11 + os: ubuntu-22.04 - toolset: gcc-12 cxxstd: "03,11,14,17,20,2b" os: ubuntu-22.04 @@ -159,9 +158,6 @@ jobs: container: ubuntu:24.04 os: ubuntu-latest install: clang-18 - - toolset: clang - cxxstd: "03,11,14,17,2a" - os: macos-11 - toolset: clang cxxstd: "03,11,14,17,20,2b" os: macos-12 @@ -293,9 +289,9 @@ jobs: include: - os: ubuntu-20.04 - os: ubuntu-22.04 - - os: macos-11 - os: macos-12 - os: macos-13 + - os: macos-14 runs-on: ${{matrix.os}} @@ -342,9 +338,9 @@ jobs: include: - os: ubuntu-20.04 - os: ubuntu-22.04 - - os: macos-11 - os: macos-12 - os: macos-13 + - os: macos-14 runs-on: ${{matrix.os}} @@ -401,9 +397,9 @@ jobs: include: - os: ubuntu-20.04 - os: ubuntu-22.04 - - os: macos-11 - os: macos-12 - os: macos-13 + - os: macos-14 runs-on: ${{matrix.os}} From 6a0f933b2ead513040dc3bfd3ff08ed0204e1138 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Wed, 19 Jun 2024 03:38:32 +0300 Subject: [PATCH 2/6] Update BOOST_MSVC workarounds for msvc-14.3 19.40 --- test/bind_back_md_constexpr_test.cpp | 2 +- test/bind_front_md_constexpr_test.cpp | 2 +- test/invoke_md_constexpr_test.cpp | 2 +- test/invoke_r_md_constexpr_test.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/bind_back_md_constexpr_test.cpp b/test/bind_back_md_constexpr_test.cpp index 018a155..13731e2 100644 --- a/test/bind_back_md_constexpr_test.cpp +++ b/test/bind_back_md_constexpr_test.cpp @@ -39,7 +39,7 @@ int main() BOOST_TEST_EQ( boost::compat::bind_back( &X::m, &x )(), -1 ); } -#if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1940) +#if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1950) { BOOST_TEST_EQ( boost::compat::bind_back( &X::m, Y() )(), -1 ); diff --git a/test/bind_front_md_constexpr_test.cpp b/test/bind_front_md_constexpr_test.cpp index c66773b..0dc444d 100644 --- a/test/bind_front_md_constexpr_test.cpp +++ b/test/bind_front_md_constexpr_test.cpp @@ -39,7 +39,7 @@ int main() BOOST_TEST_EQ( boost::compat::bind_front( &X::m, &x )(), -1 ); } -#if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1940) +#if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1950) { BOOST_TEST_EQ( boost::compat::bind_front( &X::m, Y() )(), -1 ); diff --git a/test/invoke_md_constexpr_test.cpp b/test/invoke_md_constexpr_test.cpp index 11e38f4..3c5622b 100644 --- a/test/invoke_md_constexpr_test.cpp +++ b/test/invoke_md_constexpr_test.cpp @@ -29,7 +29,7 @@ int main() BOOST_TEST_EQ( boost::compat::invoke( &X::m, &x ), -1 ); } -#if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1940) +#if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1950) { BOOST_TEST_EQ( boost::compat::invoke( &X::m, Y() ), -1 ); diff --git a/test/invoke_r_md_constexpr_test.cpp b/test/invoke_r_md_constexpr_test.cpp index ba3526e..bd969c4 100644 --- a/test/invoke_r_md_constexpr_test.cpp +++ b/test/invoke_r_md_constexpr_test.cpp @@ -36,7 +36,7 @@ int main() #endif } -#if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1940) +#if !BOOST_WORKAROUND(BOOST_MSVC, >= 1920 && BOOST_MSVC < 1950) { constexpr Y y = {}; From b9f5082f6e74b086edea514a9fe9f6d8a5b039e4 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 20 Jun 2024 11:05:25 -0700 Subject: [PATCH 3/6] expand fn object function_ref tests --- test/function_ref_obj_noexcept_test.cpp | 47 +++++++++++++++ test/function_ref_obj_test.cpp | 77 +++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/test/function_ref_obj_noexcept_test.cpp b/test/function_ref_obj_noexcept_test.cpp index ddab3ec..19b2dba 100644 --- a/test/function_ref_obj_noexcept_test.cpp +++ b/test/function_ref_obj_noexcept_test.cpp @@ -67,25 +67,36 @@ int main() { BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); 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 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 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&&>)); 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 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&&>)); BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const>)); BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const&>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const&&>)); } { using S1 = int() const noexcept; @@ -95,25 +106,57 @@ int main() { BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); 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 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&&>)); 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 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&&>)); 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 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&&>)); BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const>)); BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const&>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const&&>)); + } + + { + using S2 = int(int) noexcept; + using S4 = int(int, int, int) noexcept; + + auto& fref = f1; + + compat::function_ref fv2(fref); + BOOST_TEST_EQ(fv2(1), 1); + + auto const& cfref = f1; + compat::function_ref fv4(cfref); + BOOST_TEST_EQ(fv4(1, 2, 3), 123); + } + + { + using S4 = int(int, int, int) const noexcept; + + auto const& cfref = f1; + compat::function_ref fv4(cfref); + BOOST_TEST_EQ(fv4(1, 2, 3), 123); } } @@ -123,13 +166,17 @@ int main() { BOOST_TEST_TRAIT_FALSE((std::is_constructible, F2>)); 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 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&&>)); 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 const&&>)); } // invoke_r diff --git a/test/function_ref_obj_test.cpp b/test/function_ref_obj_test.cpp index 3136ea8..e9b4e62 100644 --- a/test/function_ref_obj_test.cpp +++ b/test/function_ref_obj_test.cpp @@ -59,30 +59,39 @@ int main() { 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&>)); + 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&&>)); BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const>)); 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&&>)); BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const>)); BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const&>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const&&>)); } { @@ -93,29 +102,89 @@ int main() { BOOST_TEST_TRAIT_FALSE((std::is_constructible, F1>)); 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 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&&>)); 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 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&&>)); BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const>)); 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&&>)); BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const>)); BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const&>)); + BOOST_TEST_TRAIT_TRUE((std::is_constructible, F1 const&&>)); + } + + { + using S1 = int(); + using S2 = int(int); + + auto& fref = f; + + compat::function_ref fv1(fref); + BOOST_TEST_EQ(fv1(), -1); + + compat::function_ref fv2(fref); + BOOST_TEST_EQ(fv2(1), 1); + } + + { + using S1 = int(); + using S2 = int(int); + + compat::function_ref fv1(std::move(f)); + BOOST_TEST_EQ(fv1(), -1); + + compat::function_ref fv2(std::move(f)); + BOOST_TEST_EQ(fv2(1), 1); + } + + { + using S3 = int(int, int) const; + using S4 = int(int, int, int) const; + + auto const& fref = f; + + compat::function_ref fv3(fref); + BOOST_TEST_EQ(fv3(1, 2), 12); + + compat::function_ref fv4(fref); + BOOST_TEST_EQ(fv4(1, 2, 3), 123); + } + + { + using S3 = int(int, int) const; + using S4 = int(int, int, int) const; + + auto const&& fref = std::move(f); + + compat::function_ref fv3(fref); + BOOST_TEST_EQ(fv3(1, 2), 12); + + compat::function_ref fv4(fref); + BOOST_TEST_EQ(fv4(1, 2, 3), 123); } } @@ -125,12 +194,20 @@ int main() { compat::function_ref fv1(g); BOOST_TEST_EQ(fv1(3, 2), 321); + auto& gref = g; + compat::function_ref rfv1(gref); + BOOST_TEST_EQ(rfv1(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); + auto const& gcref = g; + compat::function_ref crfv3(gcref); + BOOST_TEST_EQ(fv3(3, 2), 322); + compat::function_ref fv4(std::move(g)); BOOST_TEST_EQ(fv4(3, 2), 322); } From 2a5e8031f8b6241efe83af0c634419ac16945c9e Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 20 Jun 2024 11:06:18 -0700 Subject: [PATCH 4/6] fix erroneous handling of const types in function_ref callable constructor --- include/boost/compat/function_ref.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/compat/function_ref.hpp b/include/boost/compat/function_ref.hpp index cd1f353..a079037 100644 --- a/include/boost/compat/function_ref.hpp +++ b/include/boost/compat/function_ref.hpp @@ -62,7 +62,7 @@ public: template function_ref_base(obj_tag, F&& fn) noexcept : thunk_{}, invoke_(&invoke_object_holder::invoke_object) { - thunk_.pobj_ = static_cast(std::addressof(fn)); + thunk_.pobj_ = const_cast(static_cast(std::addressof(fn))); } function_ref_base(const function_ref_base&) noexcept = default; From 8ed23dd6a79ff6f418ff17f679876c0e8811d3f1 Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Thu, 13 Jun 2024 16:00:10 -0700 Subject: [PATCH 5/6] implement pointer-to-member function_ref overloads --- doc/compat/function_ref.adoc | 73 ++++++++++ include/boost/compat/function_ref.hpp | 127 ++++++++++++++++- test/Jamfile | 2 + test/function_ref_mfn_noexcept_test.cpp | 164 ++++++++++++++++++++++ test/function_ref_mfn_test.cpp | 176 ++++++++++++++++++++++++ 5 files changed, 540 insertions(+), 2 deletions(-) create mode 100644 test/function_ref_mfn_noexcept_test.cpp create mode 100644 test/function_ref_mfn_test.cpp diff --git a/doc/compat/function_ref.adoc b/doc/compat/function_ref.adoc index c40452f..556dc93 100644 --- a/doc/compat/function_ref.adoc +++ b/doc/compat/function_ref.adoc @@ -52,6 +52,9 @@ class function_ref { public: template function_ref(F*) noexcept; template function_ref(F&&) noexcept; + template function_ref(nontype_t) noexcept; + template function_ref(nontype_t, U&&) noexcept; + template function_ref(nontype_t, cv T*) noexcept; function_ref(const function_ref&) noexcept = default; function_ref& operator=(const function_ref&) noexcept = default; @@ -92,6 +95,76 @@ participates in resolution when `fn` is not a pointer-to-member or pointer-to-me + Calling the `function_ref` is expression-equivalent to: + `invoke_r(static_cast(f), call-args...)`. +### Pointer to Member Function Constructor + +```cpp +template function_ref(nontype_t) noexcept; +``` + +[horizontal] +Effects:;; Constructs a `function_ref` using the supplied pointer to member function. This overload only participates +in resolution when `f` is a pointer to member or pointer to member function. + ++ +Calling the `function_ref` is express-equivalent to: `invoke_r(f, class-args)`. +Example:;; ++ +-- +```cpp +struct point { int x = 1, y = 2; }; + +point p; +compat::function_ref f(compat::nontype_t<&point::x>{}); + +BOOST_TEST_EQ(f(p), 1); +``` +-- + +### Bound Object Constructor + +```cpp +template function_ref(nontype_t, U&&) noexcept; +``` + +[horizontal] +Effects:;; Constructs a `function_ref` using the supplied pointer to member or pointer to member function and a reference +to an object to bind it to. + ++ +This overload only participates in resolution if `is_rvalue_reference_v` is false. +Example:;; ++ +-- +```cpp +struct point { int x = 1, y = 2; }; + +point p; +compat::function_ref f(compat::nontype_t<&point::x>{}, p); + +BOOST_TEST_EQ(f(), 1); +``` +-- + +### Bound Pointer Constructor + +```cpp +template function_ref(nontype_t, cv T*) noexcept; +``` + +[horizontal] +Effects:;; Constructs a `function_ref` using the supplied pointer to member or pointer to member function and a pointer +to an object. +Example:;; ++ +-- +```cpp +struct point { int x = 1, y = 2; }; + +point p; +compat::function_ref f(compat::nontype_t<&point::x>{}, &p); + +BOOST_TEST_EQ(f(), 1); +``` +-- + ### Copy Constructor ```cpp diff --git a/include/boost/compat/function_ref.hpp b/include/boost/compat/function_ref.hpp index a079037..f906f33 100644 --- a/include/boost/compat/function_ref.hpp +++ b/include/boost/compat/function_ref.hpp @@ -8,9 +8,12 @@ #include #include -#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 { @@ -18,6 +21,15 @@ 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 @@ -43,6 +55,34 @@ struct invoke_object_holder { } }; +#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: @@ -52,6 +92,7 @@ private: public: struct fp_tag {}; struct obj_tag {}; + struct mem_fn_tag {}; template function_ref_base(fp_tag, F* fn) noexcept @@ -65,6 +106,28 @@ public: 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; @@ -78,6 +141,7 @@ struct function_ref : public detail::function_ref_base; using typename base_type::fp_tag; + using typename base_type::mem_fn_tag; using typename base_type::obj_tag; template @@ -93,6 +157,20 @@ public: 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) noexcept : base_type(mem_fn_tag{}, nontype_t{}) {} + + template , class F = decltype(f), + enable_if_t && is_invocable_using::value, int> = 0> + function_ref(nontype_t, U&& obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, std::forward(obj)) {} + + template ::value, int> = 0> + function_ref(nontype_t, T* obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, obj) {} + +#endif + function_ref(const function_ref&) noexcept = default; function_ref& operator=(const function_ref&) noexcept = default; @@ -105,6 +183,7 @@ struct function_ref : public detail::function_ref_base; using typename base_type::fp_tag; + using typename base_type::mem_fn_tag; using typename base_type::obj_tag; template @@ -120,6 +199,20 @@ public: 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) noexcept : base_type(mem_fn_tag{}, nontype_t{}) {} + + template , class F = decltype(f), + enable_if_t && is_invocable_using::value, int> = 0> + function_ref(nontype_t, U&& obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, std::forward(obj)) {} + + template ::value, int> = 0> + function_ref(nontype_t, T const* obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, obj) {} + +#endif + function_ref(const function_ref&) noexcept = default; function_ref& operator=(const function_ref&) noexcept = default; @@ -134,6 +227,7 @@ struct function_ref : public detail::function_ref_base; using typename base_type::fp_tag; + using typename base_type::mem_fn_tag; using typename base_type::obj_tag; template @@ -149,6 +243,20 @@ public: 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) noexcept : base_type(mem_fn_tag{}, nontype_t{}) {} + + template , class F = decltype(f), + enable_if_t && is_invocable_using::value, int> = 0> + function_ref(nontype_t, U&& obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, std::forward(obj)) {} + + template ::value, int> = 0> + function_ref(nontype_t, T* obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, obj) {} + +#endif + function_ref(const function_ref&) noexcept = default; function_ref& operator=(const function_ref&) noexcept = default; @@ -161,6 +269,7 @@ struct function_ref : public detail::function_ref_bas 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 @@ -176,6 +285,20 @@ public: 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) noexcept : base_type(mem_fn_tag{}, nontype_t{}) {} + + template , class F = decltype(f), + enable_if_t && is_invocable_using::value, int> = 0> + function_ref(nontype_t, U&& obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, std::forward(obj)) {} + + template ::value, int> = 0> + function_ref(nontype_t, T const* obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, obj) {} + +#endif + function_ref(const function_ref&) noexcept = default; function_ref& operator=(const function_ref&) noexcept = default; diff --git a/test/Jamfile b/test/Jamfile index 77da63a..8751f89 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -103,6 +103,8 @@ run is_nothrow_invocable_r_test.cpp ; run function_ref_fn_test.cpp ; run function_ref_obj_test.cpp ; +run function_ref_mfn_test.cpp ; run function_ref_fn_noexcept_test.cpp ; +run function_ref_mfn_noexcept_test.cpp ; run function_ref_obj_noexcept_test.cpp ; diff --git a/test/function_ref_mfn_noexcept_test.cpp b/test/function_ref_mfn_noexcept_test.cpp new file mode 100644 index 0000000..8d43699 --- /dev/null +++ b/test/function_ref_mfn_noexcept_test.cpp @@ -0,0 +1,164 @@ +// 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 + +#if !defined(BOOST_COMPAT_HAS_AUTO_NTTP) +#include +BOOST_PRAGMA_MESSAGE("no support for placeholder NTTPs detected, skipping this test") +int main() {} +#else + +#include +#include + +#include +#include + +struct F1 { + std::unique_ptr p_; + + F1() : p_(new int(1)) {} + + int m1() { return *p_ + -1; } + int m2(int x1) noexcept { return 10 * *p_ + x1; } + int m3(int x1, int x2) const { return 100 * *p_ + 10 * x1 + x2; } + int m4(int x1, int x2, int x3) const noexcept { return 1000 * *p_ + 100 * x1 + 10 * x2 + x3; } +}; + +namespace compat = boost::compat; + +int main() { + { + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m1>>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_constructible, compat::nontype_t<&F1::m2>>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m3>>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_constructible, compat::nontype_t<&F1::m4>>)); + } + + { + F1 f1; + + compat::function_ref fn2(compat::nontype_t<&F1::m2>{}); + BOOST_TEST_EQ(fn2(f1, 2), 12); + + compat::function_ref fn4(compat::nontype_t<&F1::m4>{}); + BOOST_TEST_EQ(fn4(f1, 2, 3, 4), 1234); + } + + { + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m1>>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m3>>)); + + F1 f1; + + compat::function_ref fn2(compat::nontype_t<&F1::m2>{}); + BOOST_TEST_EQ(fn2(f1, 2), 12); + + compat::function_ref fn4(compat::nontype_t<&F1::m4>{}); + BOOST_TEST_EQ(fn4(f1, 2, 3, 4), 1234); + } + + { + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m1>>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m2>>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m3>>)); + + F1 f1; + + compat::function_ref fn4(compat::nontype_t<&F1::m4>{}); + BOOST_TEST_EQ(fn4(f1, 2, 3, 4), 1234); + } + + { + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m1>, F1&>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_constructible, compat::nontype_t<&F1::m2>, F1&>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m3>, F1&>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_constructible, compat::nontype_t<&F1::m4>, F1&>)); + + F1 f1; + + compat::function_ref fn2(compat::nontype_t<&F1::m2>{}, f1); + BOOST_TEST_EQ(fn2(2), 12); + + compat::function_ref fn4(compat::nontype_t<&F1::m4>{}, f1); + BOOST_TEST_EQ(fn4(2, 3, 4), 1234); + } + + { + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m1>, F1 const&>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m2>, F1 const&>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, + compat::nontype_t<&F1::m3>, F1 const&>)); + + F1 f1; + + compat::function_ref fn4(compat::nontype_t<&F1::m4>{}, f1); + BOOST_TEST_EQ(fn4(2, 3, 4), 1234); + + auto const& f1_2 = f1; + compat::function_ref fn4_2(compat::nontype_t<&F1::m4>{}, f1_2); + BOOST_TEST_EQ(fn4_2(2, 3, 4), 1234); + } + + { + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m1>, F1*>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_constructible, compat::nontype_t<&F1::m2>, F1*>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m3>, F1*>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_constructible, compat::nontype_t<&F1::m4>, F1*>)); + + F1 f1; + + compat::function_ref fn2(compat::nontype_t<&F1::m2>{}, &f1); + BOOST_TEST_EQ(fn2(2), 12); + + compat::function_ref fn4(compat::nontype_t<&F1::m4>{}, &f1); + BOOST_TEST_EQ(fn4(2, 3, 4), 1234); + } + + { + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m1>, F1 const*>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m2>, F1 const*>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, + compat::nontype_t<&F1::m3>, F1 const*>)); + + F1 const f1; + + compat::function_ref fn4(compat::nontype_t<&F1::m4>{}, &f1); + BOOST_TEST_EQ(fn4(2, 3, 4), 1234); + + auto const& f1_2 = f1; + compat::function_ref fn4_2(compat::nontype_t<&F1::m4>{}, &f1_2); + BOOST_TEST_EQ(fn4_2(2, 3, 4), 1234); + } + + return boost::report_errors(); +} + +#endif diff --git a/test/function_ref_mfn_test.cpp b/test/function_ref_mfn_test.cpp new file mode 100644 index 0000000..17e8c11 --- /dev/null +++ b/test/function_ref_mfn_test.cpp @@ -0,0 +1,176 @@ +// 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 + +#if !defined(BOOST_COMPAT_HAS_AUTO_NTTP) +#include +BOOST_PRAGMA_MESSAGE("no support for placeholder NTTPs detected, skipping this test") +int main() {} +#else + +#include +#include + +#include +#include + +struct F1 { + std::unique_ptr p_; + int q_ = 1337; + + F1() : p_(new int(1)) {} + + int m1() { return *p_ + -1; } + int m2(int x1) noexcept { return 10 * *p_ + x1; } + int m3(int x1, int x2) const { return 100 * *p_ + 10 * x1 + x2; } + int m4(int x1, int x2, int x3) const noexcept { return 1000 * *p_ + 100 * x1 + 10 * x2 + x3; } +}; + +namespace compat = boost::compat; + +int main() { + { + F1 f1; + + compat::function_ref fn1(compat::nontype_t<&F1::m1>{}); + BOOST_TEST_EQ(fn1(f1), 0); + + compat::function_ref fn2(compat::nontype_t<&F1::m2>{}); + BOOST_TEST_EQ(fn2(f1, 2), 12); + + compat::function_ref fn3(compat::nontype_t<&F1::m3>{}); + BOOST_TEST_EQ(fn3(f1, 2, 3), 123); + + compat::function_ref fn4(compat::nontype_t<&F1::m4>{}); + BOOST_TEST_EQ(fn4(f1, 2, 3, 4), 1234); + + compat::function_ref a1(compat::nontype_t<&F1::q_>{}); + BOOST_TEST_EQ(a1(f1), 1337); + } + + { + F1 f1; + + compat::function_ref fn1(compat::nontype_t<&F1::m1>{}); + BOOST_TEST_EQ(fn1(f1), 0); + + compat::function_ref fn2(compat::nontype_t<&F1::m2>{}); + BOOST_TEST_EQ(fn2(f1, 2), 12); + + compat::function_ref fn3(compat::nontype_t<&F1::m3>{}); + BOOST_TEST_EQ(fn3(f1, 2, 3), 123); + + compat::function_ref fn4(compat::nontype_t<&F1::m4>{}); + BOOST_TEST_EQ(fn4(f1, 2, 3, 4), 1234); + } + + { + BOOST_TEST_TRAIT_FALSE((std::is_constructible, compat::nontype_t<&F1::m1>>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m2>>)); + + F1 f1; + + compat::function_ref fn3(compat::nontype_t<&F1::m3>{}); + BOOST_TEST_EQ(fn3(f1, 2, 3), 123); + + compat::function_ref fn4(compat::nontype_t<&F1::m4>{}); + BOOST_TEST_EQ(fn4(f1, 2, 3, 4), 1234); + + auto const& f2 = f1; + + compat::function_ref fn23(compat::nontype_t<&F1::m3>{}); + BOOST_TEST_EQ(fn3(f2, 2, 3), 123); + + compat::function_ref fn24(compat::nontype_t<&F1::m4>{}); + BOOST_TEST_EQ(fn4(f2, 2, 3, 4), 1234); + } + + { + BOOST_TEST_TRAIT_TRUE((std::is_constructible, compat::nontype_t<&F1::m2>, F1&>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m2>, F1 const&>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, compat::nontype_t<&F1::m2>, F1&&>)); + BOOST_TEST_TRAIT_FALSE((std::is_constructible, compat::nontype_t<&F1::m2>, F1>)); + + BOOST_TEST_TRAIT_TRUE( + (std::is_constructible, compat::nontype_t<&F1::m3>, F1&>)); + BOOST_TEST_TRAIT_TRUE( + (std::is_constructible, compat::nontype_t<&F1::m3>, F1 const&>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m3>, F1&&>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m3>, F1>)); + } + + { + F1 f1; + + compat::function_ref fn1(compat::nontype_t<&F1::m1>{}, f1); + BOOST_TEST_EQ(fn1(), 0); + + compat::function_ref fn2(compat::nontype_t<&F1::m2>{}, f1); + BOOST_TEST_EQ(fn2(2), 12); + + compat::function_ref fn3(compat::nontype_t<&F1::m3>{}, f1); + BOOST_TEST_EQ(fn3(2, 3), 123); + + compat::function_ref fn4(compat::nontype_t<&F1::m4>{}, f1); + BOOST_TEST_EQ(fn4(2, 3, 4), 1234); + } + + { + F1 const f1; + + BOOST_TEST_TRAIT_FALSE((std::is_constructible, compat::nontype_t<&F1::m1>, F1&>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m2>, F1&>)); + + compat::function_ref fn3(compat::nontype_t<&F1::m3>{}, f1); + BOOST_TEST_EQ(fn3(2, 3), 123); + + compat::function_ref fn4(compat::nontype_t<&F1::m4>{}, f1); + BOOST_TEST_EQ(fn4(2, 3, 4), 1234); + } + + { + F1 f1; + + compat::function_ref fn1(compat::nontype_t<&F1::m1>{}, &f1); + BOOST_TEST_EQ(fn1(), 0); + + compat::function_ref fn2(compat::nontype_t<&F1::m2>{}, &f1); + BOOST_TEST_EQ(fn2(2), 12); + + compat::function_ref fn3(compat::nontype_t<&F1::m3>{}, &f1); + BOOST_TEST_EQ(fn3(2, 3), 123); + + compat::function_ref fn4(compat::nontype_t<&F1::m4>{}, &f1); + BOOST_TEST_EQ(fn4(2, 3, 4), 1234); + } + + { + F1 const f1; + + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m1>, F1 const*>)); + BOOST_TEST_TRAIT_FALSE( + (std::is_constructible, compat::nontype_t<&F1::m2>, F1 const*>)); + + compat::function_ref fn3(compat::nontype_t<&F1::m3>{}, &f1); + BOOST_TEST_EQ(fn3(2, 3), 123); + + compat::function_ref fn4(compat::nontype_t<&F1::m4>{}, &f1); + BOOST_TEST_EQ(fn4(2, 3, 4), 1234); + } + + return boost::report_errors(); +} + +#endif From 37dbb81cce71a70fa2d11ac99520467f0069b9cb Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Wed, 19 Jun 2024 13:43:50 -0700 Subject: [PATCH 6/6] add workaround for clang-7 --- include/boost/compat/function_ref.hpp | 36 +++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/include/boost/compat/function_ref.hpp b/include/boost/compat/function_ref.hpp index f906f33..701bb15 100644 --- a/include/boost/compat/function_ref.hpp +++ b/include/boost/compat/function_ref.hpp @@ -108,21 +108,21 @@ public: #if defined(BOOST_COMPAT_HAS_AUTO_NTTP) - template + template function_ref_base(mem_fn_tag, nontype_t) - : thunk_{}, invoke_(&invoke_mem_fn_holder::invoke_mem_fn) { + : thunk_{}, invoke_(&invoke_mem_fn_holder::invoke_mem_fn) { thunk_.pobj_ = nullptr; } - template + template function_ref_base(mem_fn_tag, nontype_t, U&& obj) - : thunk_{}, invoke_(&invoke_target_mem_fn_holder::invoke_mem_fn) { + : thunk_{}, invoke_(&invoke_target_mem_fn_holder::invoke_mem_fn) { thunk_.pobj_ = const_cast(static_cast(std::addressof(obj))); } - template + template function_ref_base(mem_fn_tag, nontype_t, T* obj) - : thunk_{}, invoke_(&invoke_ptr_mem_fn_holder::invoke_mem_fn) { + : thunk_{}, invoke_(&invoke_ptr_mem_fn_holder::invoke_mem_fn) { thunk_.pobj_ = const_cast(static_cast(obj)); } @@ -160,14 +160,14 @@ public: #if defined(BOOST_COMPAT_HAS_AUTO_NTTP) template ::value, int> = 0> - function_ref(nontype_t) noexcept : base_type(mem_fn_tag{}, nontype_t{}) {} + 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, U&& obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, std::forward(obj)) {} + 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, T* obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, obj) {} + function_ref(nontype_t x, T* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {} #endif @@ -202,14 +202,14 @@ public: #if defined(BOOST_COMPAT_HAS_AUTO_NTTP) template ::value, int> = 0> - function_ref(nontype_t) noexcept : base_type(mem_fn_tag{}, nontype_t{}) {} + 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, U&& obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, std::forward(obj)) {} + 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, T const* obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, obj) {} + function_ref(nontype_t x, T const* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {} #endif @@ -246,14 +246,14 @@ public: #if defined(BOOST_COMPAT_HAS_AUTO_NTTP) template ::value, int> = 0> - function_ref(nontype_t) noexcept : base_type(mem_fn_tag{}, nontype_t{}) {} + 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, U&& obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, std::forward(obj)) {} + 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, T* obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, obj) {} + function_ref(nontype_t x, T* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {} #endif @@ -288,14 +288,14 @@ public: #if defined(BOOST_COMPAT_HAS_AUTO_NTTP) template ::value, int> = 0> - function_ref(nontype_t) noexcept : base_type(mem_fn_tag{}, nontype_t{}) {} + 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, U&& obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, std::forward(obj)) {} + 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, T const* obj) noexcept : base_type(mem_fn_tag{}, nontype_t{}, obj) {} + function_ref(nontype_t x, T const* obj) noexcept : base_type(mem_fn_tag{}, x, obj) {} #endif