Skip to content

Commit 9995b81

Browse files
committed
Fix explicit_conversion_dispatch and weak_dispatch for reference-returning overloads
1 parent 3c49422 commit 9995b81

5 files changed

Lines changed: 98 additions & 10 deletions

File tree

include/proxy/v4/proxy.h

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,10 +1288,26 @@ struct converter {
12881288
template <class T>
12891289
operator T() && noexcept(
12901290
std::is_nothrow_invocable_r_v<T, F, std::in_place_type_t<T>>)
1291-
requires(std::is_invocable_r_v<T, F, std::in_place_type_t<T>>)
1291+
requires(std::is_invocable_r_v<T, F, std::in_place_type_t<T>> &&
1292+
!std::is_invocable_r_v<T, F, std::in_place_type_t<T&>> &&
1293+
!std::is_invocable_r_v<T, F, std::in_place_type_t<T &&>>)
12921294
{
12931295
return std::move(f_)(std::in_place_type<T>);
12941296
}
1297+
template <class T>
1298+
operator T&() && noexcept(
1299+
std::is_nothrow_invocable_r_v<T&, F, std::in_place_type_t<T&>>)
1300+
requires(std::is_invocable_r_v<T&, F, std::in_place_type_t<T&>>)
1301+
{
1302+
return std::move(f_)(std::in_place_type<T&>);
1303+
}
1304+
template <class T>
1305+
operator T&&() && noexcept(
1306+
std::is_nothrow_invocable_r_v<T&&, F, std::in_place_type_t<T&&>>)
1307+
requires(std::is_invocable_r_v<T &&, F, std::in_place_type_t<T &&>>)
1308+
{
1309+
return std::move(f_)(std::in_place_type<T&&>);
1310+
}
12951311

12961312
private:
12971313
F f_;
@@ -2346,14 +2362,29 @@ struct sign {
23462362
template <std::size_t N>
23472363
sign(const char (&str)[N]) -> sign<N - 1u>;
23482364

2349-
struct wildcard {
2350-
wildcard() = delete;
2365+
// When std::reference_constructs_from_temporary_v (C++23) is not available, we
2366+
// fall back to a conservative approximation that disallows binding a temporary
2367+
// to a reference type if the source type is not a reference or if the source
2368+
// and target reference types are not compatible.
2369+
template <class T, class U>
2370+
concept explicitly_convertible =
2371+
std::is_constructible_v<U, T> &&
2372+
#if __cpp_lib_reference_from_temporary >= 202202L
2373+
!std::reference_constructs_from_temporary_v<U, T>;
2374+
#else
2375+
(!std::is_reference_v<U> ||
2376+
(std::is_reference_v<T> &&
2377+
std::is_convertible_v<std::add_pointer_t<std::remove_reference_t<T>>,
2378+
std::add_pointer_t<std::remove_reference_t<U>>>));
2379+
#endif // __cpp_lib_reference_from_temporary >= 202202L
23512380

2381+
struct noreturn_conversion {
23522382
template <class T>
2353-
[[noreturn]] operator T() const {
2383+
[[noreturn]] PRO4D_STATIC_CALL(T, std::in_place_type_t<T>) {
23542384
PROD_UNREACHABLE();
23552385
}
23562386
};
2387+
using wildcard = converter<noreturn_conversion>;
23572388

23582389
} // namespace details
23592390

@@ -2577,9 +2608,9 @@ struct explicit_conversion_dispatch : details::cast_dispatch_base<true, false> {
25772608
PRO4D_STATIC_CALL(auto, T&& self) noexcept {
25782609
return details::converter{
25792610
[&self]<class U>(std::in_place_type_t<U>) noexcept(
2580-
std::is_nothrow_constructible_v<U, T>)
2581-
requires(std::is_constructible_v<U, T>)
2582-
{ return U{std::forward<T>(self)}; }};
2611+
std::is_nothrow_constructible_v<U, T>) -> U
2612+
requires(details::explicitly_convertible < T &&, U >)
2613+
{ return static_cast<U>(std::forward<T>(self)); }};
25832614
}
25842615
};
25852616
using conversion_dispatch = explicit_conversion_dispatch;

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ include(GoogleTest)
1212

1313
add_executable(msft_proxy_tests
1414
proxy_creation_tests.cpp
15+
proxy_details_tests.cpp
1516
proxy_dispatch_tests.cpp
1617
proxy_fmt_format_tests.cpp
1718
proxy_format_tests.cpp

tests/proxy_details_tests.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) 2026-Present Next Gen C++ Foundation.
2+
// Licensed under the MIT License.
3+
4+
#include <proxy/proxy.h>
5+
6+
namespace proxy_details_tests_details {
7+
8+
struct Base {
9+
int v;
10+
};
11+
struct Derived : Base {};
12+
13+
static_assert(pro::details::explicitly_convertible<int, int>);
14+
static_assert(pro::details::explicitly_convertible<long, int>);
15+
static_assert(!pro::details::explicitly_convertible<int, int&&>);
16+
static_assert(!pro::details::explicitly_convertible<int, const int&>);
17+
static_assert(pro::details::explicitly_convertible<int&&, int&&>);
18+
static_assert(pro::details::explicitly_convertible<int&&, const int&>);
19+
static_assert(!pro::details::explicitly_convertible<long&&, int&&>);
20+
static_assert(!pro::details::explicitly_convertible<long, int&&>);
21+
static_assert(pro::details::explicitly_convertible<Derived&, Base&>);
22+
static_assert(pro::details::explicitly_convertible<Derived&, const Base&>);
23+
static_assert(!pro::details::explicitly_convertible<Derived&, Base&&>);
24+
static_assert(!pro::details::explicitly_convertible<const Derived&, Base&>);
25+
static_assert(
26+
pro::details::explicitly_convertible<const Derived&, const Base&>);
27+
static_assert(pro::details::explicitly_convertible<Derived, Base>);
28+
static_assert(!pro::details::explicitly_convertible<Derived, Base&&>);
29+
static_assert(!pro::details::explicitly_convertible<Base&, Derived&>);
30+
31+
} // namespace proxy_details_tests_details

tests/proxy_dispatch_tests.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -783,11 +783,13 @@ TEST(ProxyDispatchTests, TestRhsOpPtrToMem) {
783783

784784
TEST(ProxyDispatchTests, TestIndirectConversion) {
785785
struct TestFacade
786-
: pro::facade_builder::add_convention<pro::conversion_dispatch,
787-
int()>::build {};
786+
: pro::facade_builder //
787+
::add_convention<pro::conversion_dispatch, int(), short&()> //
788+
::build {};
788789
short v = 123;
789790
pro::proxy<TestFacade> p = &v;
790-
ASSERT_EQ(static_cast<int>(*p), 123);
791+
static_cast<short&>(*p) = 456;
792+
ASSERT_EQ(static_cast<int>(*p), 456);
791793
}
792794

793795
TEST(ProxyDispatchTests, TestDirectConversion) {

tests/proxy_regression_tests.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22
// Licensed under the MIT License.
33

44
#include <gtest/gtest.h>
5+
#if defined(_MSC_VER) && !defined(__clang__)
6+
#pragma warning(push)
7+
#pragma warning( \
8+
disable : 4702) // False alarm from MSVC: warning C4702: unreachable code
9+
#endif // defined(_MSC_VER) && !defined(__clang__)
510
#include <proxy/proxy.h>
11+
#if defined(_MSC_VER) && !defined(__clang__)
12+
#pragma warning(pop)
13+
#endif // defined(_MSC_VER) && !defined(__clang__)
614
#include <vector>
715

816
namespace proxy_regression_tests_details {
@@ -39,6 +47,8 @@ struct Range : pro::facade_builder //
3947
::template add_convention<MemEnd, pro::proxy<Iterator<T>>()> //
4048
::build {};
4149

50+
PRO_DEF_MEM_DISPATCH(MemFun, Fun);
51+
4252
} // namespace proxy_regression_tests_details
4353

4454
namespace details = proxy_regression_tests_details;
@@ -69,3 +79,16 @@ TEST(ProxyRegressionTests, TestProxiableSelfDependency) {
6979
}
7080
EXPECT_EQ(expected, original);
7181
}
82+
83+
// https://github.com/ngcpp/proxy/issues/10
84+
TEST(ProxyRegressionTests, TestWeakDispathReferenceReturningOverload) {
85+
struct MyFacade
86+
: pro::facade_builder //
87+
::add_convention<pro::weak_dispatch<details::MemFun>, int&()> //
88+
::build {};
89+
static_assert(pro::proxiable<int*, MyFacade>);
90+
91+
int v = 123;
92+
pro::proxy<MyFacade> p = &v;
93+
EXPECT_THROW(p->Fun(), pro::not_implemented);
94+
}

0 commit comments

Comments
 (0)