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 2 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
65 changes: 56 additions & 9 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 @@ -516,6 +519,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 +562,44 @@ 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;

template <class D>
constexpr explicit dispatch_ptr(std::in_place_type_t<D>) noexcept
#if defined(_MSC_VER) && !defined(__clang__)
mingxwa marked this conversation as resolved.
Show resolved Hide resolved
: 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
: 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 @@ -693,16 +735,25 @@ class proxy : public details::facade_traits<F>::base {
reset();
return initialize<P>(il, std::forward<Args>(args)...);
}
template <class O>
auto operator->*(dispatch_ptr<O> dp) const noexcept {
return [this, dp]<class... Args>(Args&&... args)
noexcept(details::overload_traits<O>::is_noexcept) -> decltype(auto)
requires(details::overload_traits<O>::template matches<Args...>) {
return dp.get_dispatcher(*meta_.operator->())(
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,12 +762,8 @@ 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>
Expand Down
8 changes: 4 additions & 4 deletions tests/proxy_dispatch_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
// Licensed under the MIT License.

#include <gtest/gtest.h>
#ifdef _MSC_VER
#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
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