From 9995b81482c456a7059eaefed9b769610a9fe2f1 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Sun, 8 Mar 2026 16:24:12 +0800 Subject: [PATCH 1/2] Fix explicit_conversion_dispatch and weak_dispatch for reference-returning overloads --- include/proxy/v4/proxy.h | 45 +++++++++++++++++++++++++++----- tests/CMakeLists.txt | 1 + tests/proxy_details_tests.cpp | 31 ++++++++++++++++++++++ tests/proxy_dispatch_tests.cpp | 8 +++--- tests/proxy_regression_tests.cpp | 23 ++++++++++++++++ 5 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 tests/proxy_details_tests.cpp diff --git a/include/proxy/v4/proxy.h b/include/proxy/v4/proxy.h index 0a6eccc..a5a2c98 100644 --- a/include/proxy/v4/proxy.h +++ b/include/proxy/v4/proxy.h @@ -1288,10 +1288,26 @@ struct converter { template operator T() && noexcept( std::is_nothrow_invocable_r_v>) - requires(std::is_invocable_r_v>) + requires(std::is_invocable_r_v> && + !std::is_invocable_r_v> && + !std::is_invocable_r_v>) { return std::move(f_)(std::in_place_type); } + template + operator T&() && noexcept( + std::is_nothrow_invocable_r_v>) + requires(std::is_invocable_r_v>) + { + return std::move(f_)(std::in_place_type); + } + template + operator T&&() && noexcept( + std::is_nothrow_invocable_r_v>) + requires(std::is_invocable_r_v>) + { + return std::move(f_)(std::in_place_type); + } private: F f_; @@ -2346,14 +2362,29 @@ struct sign { template sign(const char (&str)[N]) -> sign; -struct wildcard { - wildcard() = delete; +// When std::reference_constructs_from_temporary_v (C++23) is not available, we +// fall back to a conservative approximation that disallows binding a temporary +// to a reference type if the source type is not a reference or if the source +// and target reference types are not compatible. +template +concept explicitly_convertible = + std::is_constructible_v && +#if __cpp_lib_reference_from_temporary >= 202202L + !std::reference_constructs_from_temporary_v; +#else + (!std::is_reference_v || + (std::is_reference_v && + std::is_convertible_v>, + std::add_pointer_t>>)); +#endif // __cpp_lib_reference_from_temporary >= 202202L +struct noreturn_conversion { template - [[noreturn]] operator T() const { + [[noreturn]] PRO4D_STATIC_CALL(T, std::in_place_type_t) { PROD_UNREACHABLE(); } }; +using wildcard = converter; } // namespace details @@ -2577,9 +2608,9 @@ struct explicit_conversion_dispatch : details::cast_dispatch_base { PRO4D_STATIC_CALL(auto, T&& self) noexcept { return details::converter{ [&self](std::in_place_type_t) noexcept( - std::is_nothrow_constructible_v) - requires(std::is_constructible_v) - { return U{std::forward(self)}; }}; + std::is_nothrow_constructible_v) -> U + requires(details::explicitly_convertible < T &&, U >) + { return static_cast(std::forward(self)); }}; } }; using conversion_dispatch = explicit_conversion_dispatch; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 127c21b..ff508a3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,6 +12,7 @@ include(GoogleTest) add_executable(msft_proxy_tests proxy_creation_tests.cpp + proxy_details_tests.cpp proxy_dispatch_tests.cpp proxy_fmt_format_tests.cpp proxy_format_tests.cpp diff --git a/tests/proxy_details_tests.cpp b/tests/proxy_details_tests.cpp new file mode 100644 index 0000000..a10cd4f --- /dev/null +++ b/tests/proxy_details_tests.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2026-Present Next Gen C++ Foundation. +// Licensed under the MIT License. + +#include + +namespace proxy_details_tests_details { + +struct Base { + int v; +}; +struct Derived : Base {}; + +static_assert(pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert( + pro::details::explicitly_convertible); +static_assert(pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); +static_assert(!pro::details::explicitly_convertible); + +} // namespace proxy_details_tests_details diff --git a/tests/proxy_dispatch_tests.cpp b/tests/proxy_dispatch_tests.cpp index 0e66170..097af56 100644 --- a/tests/proxy_dispatch_tests.cpp +++ b/tests/proxy_dispatch_tests.cpp @@ -783,11 +783,13 @@ TEST(ProxyDispatchTests, TestRhsOpPtrToMem) { TEST(ProxyDispatchTests, TestIndirectConversion) { struct TestFacade - : pro::facade_builder::add_convention::build {}; + : pro::facade_builder // + ::add_convention // + ::build {}; short v = 123; pro::proxy p = &v; - ASSERT_EQ(static_cast(*p), 123); + static_cast(*p) = 456; + ASSERT_EQ(static_cast(*p), 456); } TEST(ProxyDispatchTests, TestDirectConversion) { diff --git a/tests/proxy_regression_tests.cpp b/tests/proxy_regression_tests.cpp index 5c21917..8da1f89 100644 --- a/tests/proxy_regression_tests.cpp +++ b/tests/proxy_regression_tests.cpp @@ -2,7 +2,15 @@ // Licensed under the MIT License. #include +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) +#pragma warning( \ + disable : 4702) // False alarm from MSVC: warning C4702: unreachable code +#endif // defined(_MSC_VER) && !defined(__clang__) #include +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(pop) +#endif // defined(_MSC_VER) && !defined(__clang__) #include namespace proxy_regression_tests_details { @@ -39,6 +47,8 @@ struct Range : pro::facade_builder // ::template add_convention>()> // ::build {}; +PRO_DEF_MEM_DISPATCH(MemFun, Fun); + } // namespace proxy_regression_tests_details namespace details = proxy_regression_tests_details; @@ -69,3 +79,16 @@ TEST(ProxyRegressionTests, TestProxiableSelfDependency) { } EXPECT_EQ(expected, original); } + +// https://github.com/ngcpp/proxy/issues/10 +TEST(ProxyRegressionTests, TestWeakDispathReferenceReturningOverload) { + struct MyFacade + : pro::facade_builder // + ::add_convention, int&()> // + ::build {}; + static_assert(pro::proxiable); + + int v = 123; + pro::proxy p = &v; + EXPECT_THROW(p->Fun(), pro::not_implemented); +} From 8d93aefd17bc03ca2031d5f49e497dd4c78a4da0 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Thu, 12 Mar 2026 13:31:05 +0800 Subject: [PATCH 2/2] Resolve comments --- tests/proxy_regression_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/proxy_regression_tests.cpp b/tests/proxy_regression_tests.cpp index 8da1f89..696a5b0 100644 --- a/tests/proxy_regression_tests.cpp +++ b/tests/proxy_regression_tests.cpp @@ -81,7 +81,7 @@ TEST(ProxyRegressionTests, TestProxiableSelfDependency) { } // https://github.com/ngcpp/proxy/issues/10 -TEST(ProxyRegressionTests, TestWeakDispathReferenceReturningOverload) { +TEST(ProxyRegressionTests, TestWeakDispatchReferenceReturningOverload) { struct MyFacade : pro::facade_builder // ::add_convention, int&()> //