Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement pointer-to-member support #111

Merged
merged 4 commits into from
Jun 6, 2024
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
78 changes: 63 additions & 15 deletions proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,11 @@ inline void destruction_default_dispatcher(std::byte*) noexcept {}

template <bool NE, class R, class... Args>
struct overload_traits_impl : applicable_traits {
using dispatcher_type = func_ptr_t<NE, R, const std::byte*, Args...>;
template <class D>
struct meta_provider {
template <class P>
static constexpr func_ptr_t<NE, R, const std::byte*, Args...> get() {
static constexpr dispatcher_type get() {
if constexpr (invocable_dispatch<
D, NE, R, typename ptr_traits<P>::target_type&, Args...>) {
return &invocation_dispatcher_ref<P, D, R, Args...>;
Expand All @@ -223,6 +224,8 @@ struct overload_traits_impl : applicable_traits {
static constexpr bool applicable_ptr =
invocable_dispatch_ptr<D, P, NE, R, Args...>;
static constexpr bool is_noexcept = NE;
template <class... Args2>
static constexpr bool matches = std::is_invocable_v<resolver, Args2...>;
};
template <class O> struct overload_traits : inapplicable_traits {};
template <class R, class... Args>
Expand Down Expand Up @@ -492,11 +495,8 @@ struct meta_ptr<M> : M {

template <class F>
struct proxy_helper {
template <class M>
static const M& get_meta(const proxy<F>& p) noexcept
{ return *static_cast<const M*>(p.meta_.operator->()); }
static inline const std::byte* get_ptr(const proxy<F>& p) noexcept
{ return p.ptr_; }
static inline const auto& get_meta(const proxy<F>& p) noexcept
{ return *p.meta_.operator->(); }
};
template <class F, class D, class... Args>
using proxy_overload = typename facade_traits<
Expand All @@ -516,6 +516,7 @@ class proxy : public details::facade_traits<F>::base {
friend struct details::proxy_helper<F>;
using Traits = details::facade_traits<F>;
static_assert(Traits::applicable);
using Meta = typename Traits::meta;

template <class P, class... Args>
static constexpr bool HasNothrowPolyConstructor = std::conditional_t<
Expand Down Expand Up @@ -558,6 +559,46 @@ class proxy : public details::facade_traits<F>::base {
static constexpr bool HasMoveAssignment = HasMoveConstructor && HasDestructor;

public:
template <class O>
class dispatch_ptr {
using OverloadTraits = details::overload_traits<O>;
using DispatcherType = typename OverloadTraits::dispatcher_type;

public:
constexpr dispatch_ptr() noexcept = default;
constexpr dispatch_ptr(const dispatch_ptr&) noexcept = default;
dispatch_ptr& operator=(const dispatch_ptr&) noexcept = default;

#if defined(_MSC_VER) && !defined(__clang__)
template <class D>
constexpr explicit dispatch_ptr(std::in_place_type_t<D>) noexcept
: offset_(offsetof(Meta, template dispatcher_meta<typename
OverloadTraits::template meta_provider<D>>::dispatcher)) {}
DispatcherType get_dispatcher(const Meta& meta) const noexcept {
return *reinterpret_cast<const DispatcherType*>(
reinterpret_cast<const std::byte*>(&meta) + offset_);
}

private:
std::size_t offset_;
#else
template <class D>
constexpr explicit dispatch_ptr(std::in_place_type_t<D>) noexcept
: ptr_(&details::dispatcher_meta<typename OverloadTraits
::template meta_provider<D>>::dispatcher) {}
DispatcherType get_dispatcher(const Meta& meta) const noexcept
{ return meta.*ptr_; }

private:
DispatcherType Meta::* ptr_;
#endif // defined(_MSC_VER) && !defined(__clang__)
};
template <class D, class... Args>
static auto consteval get_dispatch_ptr()
requires(requires { typename details::proxy_overload<F, D, Args...>; }) {
return dispatch_ptr<details::proxy_overload<F, D, Args...>>{
std::in_place_type<D>};
}
proxy() noexcept = default;
proxy(std::nullptr_t) noexcept : proxy() {}
proxy(const proxy& rhs) noexcept(HasNothrowCopyConstructor)
Expand Down Expand Up @@ -694,15 +735,25 @@ class proxy : public details::facade_traits<F>::base {
return initialize<P>(il, std::forward<Args>(args)...);
}

template <class O>
friend auto operator->*(const proxy& p, dispatch_ptr<O> ptd) noexcept {
return [&p, ptd]<class... Args>(Args&&... args)
noexcept(details::overload_traits<O>::is_noexcept) -> decltype(auto)
requires(details::overload_traits<O>::template matches<Args...>) {
return ptd.get_dispatcher(*p.meta_.operator->())(
p.ptr_, std::forward<Args>(args)...);
};
}

private:
template <class P, class... Args>
P& initialize(Args&&... args) {
std::construct_at(reinterpret_cast<P*>(ptr_), std::forward<Args>(args)...);
meta_ = details::meta_ptr<typename Traits::meta>{std::in_place_type<P>};
meta_ = details::meta_ptr<Meta>{std::in_place_type<P>};
return *std::launder(reinterpret_cast<P*>(ptr_));
}

details::meta_ptr<typename Traits::meta> meta_;
details::meta_ptr<Meta> meta_;
alignas(F::constraints.max_align) std::byte ptr_[F::constraints.max_size];
};

Expand All @@ -711,18 +762,14 @@ decltype(auto) proxy_invoke(const proxy<F>& p, Args&&... args)
noexcept(details::overload_traits<details::proxy_overload<F, D, Args...>>
::is_noexcept)
requires(requires { typename details::proxy_overload<F, D, Args...>; }) {
return details::proxy_helper<F>
::template get_meta<details::dispatcher_meta<
typename details::overload_traits<details::proxy_overload<
F, D, Args...>>::template meta_provider<D>>>(p)
.dispatcher(details::proxy_helper<F>::get_ptr(p),
std::forward<Args>(args)...);
constexpr auto ptd = proxy<F>::template get_dispatch_ptr<D, Args...>();
return (p->*ptd)(std::forward<Args>(args)...);
}

template <class R, class F>
const R& proxy_reflect(const proxy<F>& p) noexcept
requires(details::facade_traits<F>::template has_refl<R>)
{ return details::proxy_helper<F>::template get_meta<R>(p); }
{ return details::proxy_helper<F>::get_meta(p); }

namespace details {

Expand Down Expand Up @@ -1282,6 +1329,7 @@ ___PRO_OPERATOR_DISPATCH_TRAITS_ASSIGNMENT_IMPL(^=)
___PRO_OPERATOR_DISPATCH_TRAITS_ASSIGNMENT_IMPL(<<=)
___PRO_OPERATOR_DISPATCH_TRAITS_ASSIGNMENT_IMPL(>>=)
___PRO_OPERATOR_DISPATCH_TRAITS_BINARY_IMPL(,)
___PRO_OPERATOR_DISPATCH_TRAITS_BINARY_IMPL(->*)

#undef ___PRO_OPERATOR_DISPATCH_TRAITS_EXTENDED_BINARY_IMPL
#undef ___PRO_OPERATOR_DISPATCH_TRAITS_BINARY_IMPL
Expand Down
50 changes: 45 additions & 5 deletions tests/proxy_dispatch_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
// Licensed under the MIT License.

#include <gtest/gtest.h>
#ifdef _MSC_VER
#include <vector>
#if defined(_MSC_VER) && !defined(__clang__)
#pragma warning(push)
#pragma warning(disable: 4834) // False alarm from MSVC: warning C4834: discarding return value of function with [[nodiscard]] attribute
#endif // _MSC_VER
#endif // defined(_MSC_VER) && !defined(__clang__)
#include "proxy.h"
#ifdef _MSC_VER
#if defined(_MSC_VER) && !defined(__clang__)
#pragma warning(pop)
#endif // _MSC_VER
#endif // defined(_MSC_VER) && !defined(__clang__)

namespace {

Expand Down Expand Up @@ -46,6 +47,7 @@ PRO_DEF_OPERATOR_DISPATCH(OpBitwiseXorAssignment, "^=");
PRO_DEF_OPERATOR_DISPATCH(OpLeftShiftAssignment, "<<=");
PRO_DEF_OPERATOR_DISPATCH(OpRightShiftAssignment, ">>=");
PRO_DEF_OPERATOR_DISPATCH(OpComma, ",");
PRO_DEF_OPERATOR_DISPATCH(OpPtrToMem, "->*");
PRO_DEF_OPERATOR_DISPATCH(OpArrow, "->");
PRO_DEF_OPERATOR_DISPATCH(OpParentheses, "()");
PRO_DEF_OPERATOR_DISPATCH(OpBrackets, "[]");
Expand All @@ -72,19 +74,28 @@ PRO_DEF_PREFIX_OPERATOR_DISPATCH(PreOpCaret, "^");
PRO_DEF_PREFIX_OPERATOR_DISPATCH(PreOpLeftShift, "<<");
PRO_DEF_PREFIX_OPERATOR_DISPATCH(PreOpRightShift, ">>");
PRO_DEF_PREFIX_OPERATOR_DISPATCH(PreOpComma, ",");
PRO_DEF_PREFIX_OPERATOR_DISPATCH(PreOpPtrToMem, "->*");

PRO_DEF_CONVERTION_DISPATCH(ConvertToInt, int);

struct CommaTester {
public:
explicit CommaTester(int v) : value_(v) {}
int operator,(int v) { return value_ + v; }
friend int operator,(int v, CommaTester c) { return v * c.value_; }
friend int operator,(int v, CommaTester self) { return v * self.value_; }

private:
int value_;
};

struct PtrToMemTester {
public:
explicit PtrToMemTester(int v) : value_(v) {}
friend int operator->*(int v, PtrToMemTester self) { return v * self.value_; }

private:
int value_;
};
} // namespace

TEST(ProxyDispatchTests, TestOpPlus) {
Expand Down Expand Up @@ -340,6 +351,28 @@ TEST(ProxyDispatchTests, TestOpComma) {
ASSERT_EQ((p, 6), 9);
}

TEST(ProxyDispatchTests, TestOpPtrToMem) {
struct Base1 { int a; int b; int c; };
struct Base2 { double x; };
struct Derived1 : Base1 { int x; };
struct Derived2 : Base2, Base1 { int d; };
struct TestFacade : pro::facade_builder::add_convention<OpPtrToMem, int&(int Base1::* ptm)>::build {};
Derived1 v1{};
Derived2 v2{};
pro::proxy<TestFacade> p1 = &v1, p2 = &v2;
std::vector<int Base1::*> fields{&Base1::a, &Base1::b, &Base1::c};
for (int i = 0; i < std::ssize(fields); ++i) {
p1->*fields[i] = i + 1;
p2->*fields[i] = i + 1;
}
ASSERT_EQ(v1.a, 1);
ASSERT_EQ(v1.b, 2);
ASSERT_EQ(v1.c, 3);
ASSERT_EQ(v2.a, 1);
ASSERT_EQ(v2.b, 2);
ASSERT_EQ(v2.c, 3);
}

TEST(ProxyDispatchTests, TestOpArrow) {
struct TestFacade : pro::facade_builder::add_convention<OpArrow, const void*()>::build {};
int v = 12;
Expand Down Expand Up @@ -539,6 +572,13 @@ TEST(ProxyDispatchTests, TestPreOpComma) {
ASSERT_EQ((7, p), 21);
}

TEST(ProxyDispatchTests, TestPreOpPtrToMem) {
struct TestFacade : pro::facade_builder::add_convention<PreOpPtrToMem, int(int val)>::build {};
PtrToMemTester v{3};
pro::proxy<TestFacade> p = &v;
ASSERT_EQ(2->*p, 6);
}

TEST(ProxyDispatchTests, TestConvertion) {
struct TestFacade : pro::facade_builder::add_convention<ConvertToInt, int()>::build {};
double v = 12.3;
Expand Down
10 changes: 10 additions & 0 deletions tests/proxy_invocation_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,13 @@ TEST(ProxyInvocationTests, TestObserverDispatch) {
ASSERT_EQ(ToString(p), "123");
}

TEST(ProxyInvocationTests, TestPtrToMember) {
using Runnable = pro::proxy<spec::Callable<void()>>;
constexpr auto OpCallPtr = Runnable::get_dispatch_ptr<spec::OpCall>();
static_assert(std::is_same_v<decltype(OpCallPtr), const Runnable::dispatch_ptr<void()>>);
int side_effects = 0;
auto f = [&] { side_effects = 1; };
Runnable p = &f;
(p->*OpCallPtr)();
mingxwa marked this conversation as resolved.
Show resolved Hide resolved
ASSERT_EQ(side_effects, 1);
}
Loading