Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 38 additions & 7 deletions include/proxy/v4/proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -1288,10 +1288,26 @@ struct converter {
template <class T>
operator T() && noexcept(
std::is_nothrow_invocable_r_v<T, F, std::in_place_type_t<T>>)
requires(std::is_invocable_r_v<T, F, std::in_place_type_t<T>>)
requires(std::is_invocable_r_v<T, F, std::in_place_type_t<T>> &&
!std::is_invocable_r_v<T, F, std::in_place_type_t<T&>> &&
!std::is_invocable_r_v<T, F, std::in_place_type_t<T &&>>)
{
return std::move(f_)(std::in_place_type<T>);
}
template <class T>
operator T&() && noexcept(
std::is_nothrow_invocable_r_v<T&, F, std::in_place_type_t<T&>>)
requires(std::is_invocable_r_v<T&, F, std::in_place_type_t<T&>>)
{
return std::move(f_)(std::in_place_type<T&>);
}
template <class T>
operator T&&() && noexcept(
std::is_nothrow_invocable_r_v<T&&, F, std::in_place_type_t<T&&>>)
requires(std::is_invocable_r_v<T &&, F, std::in_place_type_t<T &&>>)
{
return std::move(f_)(std::in_place_type<T&&>);
}

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

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 <class T, class U>
concept explicitly_convertible =
std::is_constructible_v<U, T> &&
#if __cpp_lib_reference_from_temporary >= 202202L
!std::reference_constructs_from_temporary_v<U, T>;
#else
(!std::is_reference_v<U> ||
(std::is_reference_v<T> &&
std::is_convertible_v<std::add_pointer_t<std::remove_reference_t<T>>,
std::add_pointer_t<std::remove_reference_t<U>>>));
#endif // __cpp_lib_reference_from_temporary >= 202202L

struct noreturn_conversion {
template <class T>
[[noreturn]] operator T() const {
[[noreturn]] PRO4D_STATIC_CALL(T, std::in_place_type_t<T>) {
PROD_UNREACHABLE();
}
};
using wildcard = converter<noreturn_conversion>;

} // namespace details

Expand Down Expand Up @@ -2577,9 +2608,9 @@ struct explicit_conversion_dispatch : details::cast_dispatch_base<true, false> {
PRO4D_STATIC_CALL(auto, T&& self) noexcept {
return details::converter{
[&self]<class U>(std::in_place_type_t<U>) noexcept(
std::is_nothrow_constructible_v<U, T>)
requires(std::is_constructible_v<U, T>)
{ return U{std::forward<T>(self)}; }};
std::is_nothrow_constructible_v<U, T>) -> U
requires(details::explicitly_convertible < T &&, U >)
{ return static_cast<U>(std::forward<T>(self)); }};
}
};
using conversion_dispatch = explicit_conversion_dispatch;
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 31 additions & 0 deletions tests/proxy_details_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2026-Present Next Gen C++ Foundation.
// Licensed under the MIT License.

#include <proxy/proxy.h>

namespace proxy_details_tests_details {

struct Base {
int v;
};
struct Derived : Base {};

static_assert(pro::details::explicitly_convertible<int, int>);
static_assert(pro::details::explicitly_convertible<long, int>);
static_assert(!pro::details::explicitly_convertible<int, int&&>);
static_assert(!pro::details::explicitly_convertible<int, const int&>);
static_assert(pro::details::explicitly_convertible<int&&, int&&>);
static_assert(pro::details::explicitly_convertible<int&&, const int&>);
static_assert(!pro::details::explicitly_convertible<long&&, int&&>);
static_assert(!pro::details::explicitly_convertible<long, int&&>);
static_assert(pro::details::explicitly_convertible<Derived&, Base&>);
static_assert(pro::details::explicitly_convertible<Derived&, const Base&>);
static_assert(!pro::details::explicitly_convertible<Derived&, Base&&>);
static_assert(!pro::details::explicitly_convertible<const Derived&, Base&>);
static_assert(
pro::details::explicitly_convertible<const Derived&, const Base&>);
static_assert(pro::details::explicitly_convertible<Derived, Base>);
static_assert(!pro::details::explicitly_convertible<Derived, Base&&>);
static_assert(!pro::details::explicitly_convertible<Base&, Derived&>);

} // namespace proxy_details_tests_details
8 changes: 5 additions & 3 deletions tests/proxy_dispatch_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -783,11 +783,13 @@ TEST(ProxyDispatchTests, TestRhsOpPtrToMem) {

TEST(ProxyDispatchTests, TestIndirectConversion) {
struct TestFacade
: pro::facade_builder::add_convention<pro::conversion_dispatch,
int()>::build {};
: pro::facade_builder //
::add_convention<pro::conversion_dispatch, int(), short&()> //
::build {};
short v = 123;
pro::proxy<TestFacade> p = &v;
ASSERT_EQ(static_cast<int>(*p), 123);
static_cast<short&>(*p) = 456;
ASSERT_EQ(static_cast<int>(*p), 456);
}

TEST(ProxyDispatchTests, TestDirectConversion) {
Expand Down
23 changes: 23 additions & 0 deletions tests/proxy_regression_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@
// Licensed under the MIT License.

#include <gtest/gtest.h>
#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 <proxy/proxy.h>
#if defined(_MSC_VER) && !defined(__clang__)
#pragma warning(pop)
#endif // defined(_MSC_VER) && !defined(__clang__)
#include <vector>

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

PRO_DEF_MEM_DISPATCH(MemFun, Fun);

} // namespace proxy_regression_tests_details

namespace details = proxy_regression_tests_details;
Expand Down Expand Up @@ -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<pro::weak_dispatch<details::MemFun>, int&()> //
::build {};
static_assert(pro::proxiable<int*, MyFacade>);

int v = 123;
pro::proxy<MyFacade> p = &v;
EXPECT_THROW(p->Fun(), pro::not_implemented);
}