Skip to content

Commit 3ef8001

Browse files
authored
Fix weak_dispatch and explicit_conversion_dispatch in reference-returning overloads (#13)
* Fix explicit_conversion_dispatch and weak_dispatch for reference-returning overloads * Resolve comments
1 parent 1a3f464 commit 3ef8001

File tree

5 files changed

+98
-10
lines changed

5 files changed

+98
-10
lines changed

include/proxy/v4/proxy.h

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,10 +1289,26 @@ struct converter {
12891289
template <class T>
12901290
operator T() && noexcept(
12911291
std::is_nothrow_invocable_r_v<T, F, std::in_place_type_t<T>>)
1292-
requires(std::is_invocable_r_v<T, F, std::in_place_type_t<T>>)
1292+
requires(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&>> &&
1294+
!std::is_invocable_r_v<T, F, std::in_place_type_t<T &&>>)
12931295
{
12941296
return std::move(f_)(std::in_place_type<T>);
12951297
}
1298+
template <class T>
1299+
operator T&() && noexcept(
1300+
std::is_nothrow_invocable_r_v<T&, F, std::in_place_type_t<T&>>)
1301+
requires(std::is_invocable_r_v<T&, F, std::in_place_type_t<T&>>)
1302+
{
1303+
return std::move(f_)(std::in_place_type<T&>);
1304+
}
1305+
template <class T>
1306+
operator T&&() && noexcept(
1307+
std::is_nothrow_invocable_r_v<T&&, F, std::in_place_type_t<T&&>>)
1308+
requires(std::is_invocable_r_v<T &&, F, std::in_place_type_t<T &&>>)
1309+
{
1310+
return std::move(f_)(std::in_place_type<T&&>);
1311+
}
12961312

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

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

2382+
struct noreturn_conversion {
23532383
template <class T>
2354-
[[noreturn]] operator T() const {
2384+
[[noreturn]] PRO4D_STATIC_CALL(T, std::in_place_type_t<T>) {
23552385
PROD_UNREACHABLE();
23562386
}
23572387
};
2388+
using wildcard = converter<noreturn_conversion>;
23582389

23592390
} // namespace details
23602391

@@ -2578,9 +2609,9 @@ struct explicit_conversion_dispatch : details::cast_dispatch_base<true, false> {
25782609
PRO4D_STATIC_CALL(auto, T&& self) noexcept {
25792610
return details::converter{
25802611
[&self]<class U>(std::in_place_type_t<U>) noexcept(
2581-
std::is_nothrow_constructible_v<U, T>)
2582-
requires(std::is_constructible_v<U, T>)
2583-
{ return U{std::forward<T>(self)}; }};
2612+
std::is_nothrow_constructible_v<U, T>) -> U
2613+
requires(details::explicitly_convertible < T &&, U >)
2614+
{ return static_cast<U>(std::forward<T>(self)); }};
25842615
}
25852616
};
25862617
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
@@ -784,11 +784,13 @@ TEST(ProxyDispatchTests, TestRhsOpPtrToMem) {
784784

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

794796
TEST(ProxyDispatchTests, TestDirectConversion) {

tests/proxy_regression_tests.cpp

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

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

917
namespace proxy_regression_tests_details {
@@ -40,6 +48,8 @@ struct Range : pro::facade_builder //
4048
::template add_convention<MemEnd, pro::proxy<Iterator<T>>()> //
4149
::build {};
4250

51+
PRO_DEF_MEM_DISPATCH(MemFun, Fun);
52+
4353
} // namespace proxy_regression_tests_details
4454

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

0 commit comments

Comments
 (0)