Skip to content
This repository was archived by the owner on Jan 29, 2026. It is now read-only.
Merged
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
88 changes: 38 additions & 50 deletions proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,25 +198,24 @@ class destruction_guard {
T* p_;
};

template <class P, qualifier_type Q, bool NE>
struct ptr_traits : inapplicable_traits {};
template <class P, qualifier_type Q, bool NE>
requires(requires { *std::declval<add_qualifier_t<P, Q>>(); } &&
(!NE || noexcept(*std::declval<add_qualifier_t<P, Q>>())))
struct ptr_traits<P, Q, NE> : applicable_traits
{ using target_type = decltype(*std::declval<add_qualifier_t<P, Q>>()); };
template <bool IsDirect, class P, qualifier_type Q>
struct operand_traits : add_qualifier_traits<P, Q> {};
template <class P, qualifier_type Q>
struct operand_traits<false, P, Q>
: std::type_identity<decltype(*std::declval<add_qualifier_t<P, Q>>())> {};
template <bool IsDirect, class P, qualifier_type Q>
using operand_t = typename operand_traits<IsDirect, P, Q>::type;

template <class D, bool NE, class R, class... Args>
concept invocable_dispatch = (NE && std::is_nothrow_invocable_r_v<
R, D, Args...>) || (!NE && std::is_invocable_r_v<R, D, Args...>);
template <class D, class P, qualifier_type Q, bool NE, class R, class... Args>
concept invocable_dispatch_ptr_indirect = ptr_traits<P, Q, NE>::applicable &&
invocable_dispatch<
D, NE, R, typename ptr_traits<P, Q, NE>::target_type, Args...>;
template <class D, class P, qualifier_type Q, bool NE, class R, class... Args>
concept invocable_dispatch_ptr_direct = invocable_dispatch<
D, NE, R, add_qualifier_t<P, Q>, Args...> && (Q != qualifier_type::rv ||
(NE && std::is_nothrow_destructible_v<P>) ||
template <bool IsDirect, class D, class P, qualifier_type Q, bool NE, class R,
class... Args>
concept invocable_dispatch_ptr =
(IsDirect || (requires { *std::declval<add_qualifier_t<P, Q>>(); } &&
(!NE || noexcept(*std::declval<add_qualifier_t<P, Q>>())))) &&
invocable_dispatch<D, NE, R, operand_t<IsDirect, P, Q>, Args...> &&
(Q != qualifier_type::rv || (NE && std::is_nothrow_destructible_v<P>) ||
(!NE && std::is_destructible_v<P>));

template <class D, class R, class... Args>
Expand All @@ -227,25 +226,27 @@ R invoke_dispatch(Args&&... args) {
return D{}(std::forward<Args>(args)...);
}
}
template <class D, class P, qualifier_type Q, class R, class... Args>
R indirect_conv_dispatcher(add_qualifier_t<std::byte, Q> self, Args... args)
noexcept(invocable_dispatch_ptr_indirect<D, P, Q, true, R, Args...>) {
auto& qp = *std::launder(reinterpret_cast<add_qualifier_ptr_t<P, Q>>(&self));
if constexpr (std::is_constructible_v<bool, decltype(qp)>) { assert(qp); }
return invoke_dispatch<D, R>(*std::forward<add_qualifier_t<P, Q>>(qp),
std::forward<Args>(args)...);
template <bool IsDirect, class P, qualifier_type Q, class T>
decltype(auto) get_operand(T& ptr) {
if constexpr (IsDirect) {
return std::forward<add_qualifier_t<P, Q>>(ptr);
} else {
if constexpr (std::is_constructible_v<bool, T&>) { assert(ptr); }
return *std::forward<add_qualifier_t<P, Q>>(ptr);
}
}
template <class D, class P, qualifier_type Q, class R, class... Args>
R direct_conv_dispatcher(add_qualifier_t<std::byte, Q> self, Args... args)
noexcept(invocable_dispatch_ptr_direct<D, P, Q, true, R, Args...>) {
template <bool IsDirect, class D, class P, qualifier_type Q, class R,
class... Args>
R conv_dispatcher(add_qualifier_t<std::byte, Q> self, Args... args)
noexcept(invocable_dispatch_ptr<IsDirect, D, P, Q, true, R, Args...>) {
auto& qp = *std::launder(reinterpret_cast<add_qualifier_ptr_t<P, Q>>(&self));
if constexpr (Q == qualifier_type::rv) {
destruction_guard guard{&qp};
return invoke_dispatch<D, R>(
std::forward<add_qualifier_t<P, Q>>(qp), std::forward<Args>(args)...);
return invoke_dispatch<D, R>(get_operand<IsDirect, P, Q>(qp),
std::forward<Args>(args)...);
} else {
return invoke_dispatch<D, R>(
std::forward<add_qualifier_t<P, Q>>(qp), std::forward<Args>(args)...);
return invoke_dispatch<D, R>(get_operand<IsDirect, P, Q>(qp),
std::forward<Args>(args)...);
}
}
template <class D, qualifier_type Q, class R, class... Args>
Expand All @@ -263,28 +264,16 @@ struct overload_traits_impl : applicable_traits {

template <bool IsDirect, class D, class P>
static consteval bool is_applicable_ptr() {
if constexpr (IsDirect) {
if constexpr (invocable_dispatch_ptr_direct<D, P, Q, NE, R, Args...>) {
return true;
} else {
return invocable_dispatch<D, NE, R, std::nullptr_t, Args...>;
}
if constexpr (invocable_dispatch_ptr<IsDirect, D, P, Q, NE, R, Args...>) {
return true;
} else {
if constexpr (invocable_dispatch_ptr_indirect<D, P, Q, NE, R, Args...>) {
return true;
} else {
return invocable_dispatch<D, NE, R, std::nullptr_t, Args...>;
}
return invocable_dispatch<D, NE, R, std::nullptr_t, Args...>;
}
}
template <bool IsDirect, class D, class P>
static consteval dispatcher_type get_dispatcher() {
if constexpr (!IsDirect &&
invocable_dispatch_ptr_indirect<D, P, Q, NE, R, Args...>) {
return &indirect_conv_dispatcher<D, P, Q, R, Args...>;
} else if constexpr (IsDirect &&
invocable_dispatch_ptr_direct<D, P, Q, NE, R, Args...>) {
return &direct_conv_dispatcher<D, P, Q, R, Args...>;
if constexpr (invocable_dispatch_ptr<IsDirect, D, P, Q, NE, R, Args...>) {
return &conv_dispatcher<IsDirect, D, P, Q, R, Args...>;
} else {
return &default_conv_dispatcher<D, Q, R, Args...>;
}
Expand Down Expand Up @@ -791,8 +780,7 @@ struct proxy_helper {
static decltype(auto) invoke(add_qualifier_t<proxy<F>, Q> p, Args&&... args) {
auto dispatcher = get_meta(p).template invocation_meta<IsDirect, D, O>
::dispatcher;
if constexpr (
IsDirect && overload_traits<O>::qualifier == qualifier_type::rv) {
if constexpr (overload_traits<O>::qualifier == qualifier_type::rv) {
meta_ptr_reset_guard guard{p.meta_};
return dispatcher(std::forward<add_qualifier_t<std::byte, Q>>(*p.ptr_),
std::forward<Args>(args)...);
Expand Down Expand Up @@ -1327,8 +1315,8 @@ class strong_compact_ptr
{ return std::launder(reinterpret_cast<const T*>(&this->ptr_->value)); }
T& operator*() & noexcept { return *operator->(); }
const T& operator*() const& noexcept { return *operator->(); }
T&& operator*() && noexcept { return std::move(operator->()); }
const T&& operator*() const&& noexcept { return std::move(operator->()); }
T&& operator*() && noexcept { return std::move(*operator->()); }
const T&& operator*() const&& noexcept { return std::move(*operator->()); }

private:
explicit strong_compact_ptr(Storage* ptr) noexcept
Expand Down
1 change: 1 addition & 0 deletions tests/proxy_creation_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -994,5 +994,6 @@ TEST(ProxyCreationTests, TestMakeProxyView) {
ASSERT_EQ((*p)(), 0);
ASSERT_EQ((*std::as_const(p))(), 1);
ASSERT_EQ((*std::move(p))(), 2);
p = pro::make_proxy_view<TestFacade>(test_callable);
ASSERT_EQ((*std::move(std::as_const(p)))(), 3);
}
2 changes: 2 additions & 0 deletions tests/proxy_invocation_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ TEST(ProxyInvocationTests, TestQualifiedConvention_Member) {
ASSERT_EQ((*p)(), 0);
ASSERT_EQ((*std::as_const(p))(), 1);
ASSERT_EQ((*std::move(p))(), 2);
p = pro::make_proxy<TestFacade, TestCallable>();
ASSERT_EQ((*std::move(std::as_const(p)))(), 3);
}

Expand All @@ -347,5 +348,6 @@ TEST(ProxyInvocationTests, TestQualifiedConvention_Free) {
ASSERT_EQ(Dump(*p), "is_const=false, is_ref=true, value=123");
ASSERT_EQ(Dump(*std::as_const(p)), "is_const=true, is_ref=true, value=123");
ASSERT_EQ(Dump(*std::move(p)), "is_const=false, is_ref=false, value=123");
p = pro::make_proxy<TestFacade>(123);
ASSERT_EQ(Dump(*std::move(std::as_const(p))), "is_const=true, is_ref=false, value=123");
}
3 changes: 1 addition & 2 deletions tests/proxy_rtti_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ TEST(ProxyRttiTests, TestIndirectCast_Move_Succeed) {
auto p = pro::make_proxy<details::TestFacade>(v1);
auto v2 = proxy_cast<std::vector<int>>(std::move(*p));
ASSERT_EQ(v2, v1);
v2 = proxy_cast<std::vector<int>>(std::move(*p));
ASSERT_TRUE(v2.empty());
ASSERT_FALSE(p.has_value());
}

TEST(ProxyRttiTests, TestIndirectCast_Move_Fail) {
Expand Down