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

Add support for proxy_view and complementary infrastructure #218

Merged
merged 6 commits into from
Dec 17, 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
24 changes: 24 additions & 0 deletions benchmarks/proxy_invocation_benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@
namespace {

void BM_SmallObjectInvocationViaProxy(benchmark::State& state) {
auto data = GenerateSmallObjectInvocationProxyTestData();
std::vector<pro::proxy_view<const InvocationTestFacade>> views{data.begin(), data.end()};
for (auto _ : state) {
for (auto& p : views) {
int result = p->Fun();
benchmark::DoNotOptimize(result);
}
}
}

void BM_SmallObjectInvocationViaProxyView(benchmark::State& state) {
auto data = GenerateSmallObjectInvocationProxyTestData();
for (auto _ : state) {
for (auto& p : data) {
Expand Down Expand Up @@ -37,6 +48,17 @@ void BM_LargeObjectInvocationViaProxy(benchmark::State& state) {
}
}

void BM_LargeObjectInvocationViaProxyView(benchmark::State& state) {
auto data = GenerateLargeObjectInvocationProxyTestData();
std::vector<pro::proxy_view<const InvocationTestFacade>> views{data.begin(), data.end()};
for (auto _ : state) {
for (auto& p : views) {
int result = p->Fun();
benchmark::DoNotOptimize(result);
}
}
}

void BM_LargeObjectInvocationViaVirtualFunction(benchmark::State& state) {
auto data = GenerateLargeObjectInvocationVirtualFunctionTestData();
for (auto _ : state) {
Expand All @@ -48,8 +70,10 @@ void BM_LargeObjectInvocationViaVirtualFunction(benchmark::State& state) {
}

BENCHMARK(BM_SmallObjectInvocationViaProxy);
BENCHMARK(BM_SmallObjectInvocationViaProxyView);
BENCHMARK(BM_SmallObjectInvocationViaVirtualFunction);
BENCHMARK(BM_LargeObjectInvocationViaProxy);
BENCHMARK(BM_LargeObjectInvocationViaProxyView);
BENCHMARK(BM_LargeObjectInvocationViaVirtualFunction);

} // namespace
1 change: 1 addition & 0 deletions benchmarks/proxy_invocation_benchmark_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ PRO_DEF_MEM_DISPATCH(MemFun, Fun);

struct InvocationTestFacade : pro::facade_builder
::add_convention<MemFun, int() const>
::add_view<const InvocationTestFacade>
::build{};

struct InvocationTestBase {
Expand Down
162 changes: 142 additions & 20 deletions proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ using add_qualifier_ptr_t = std::remove_reference_t<add_qualifier_t<T, Q>>*;

template <template <class, class> class R, class O, class... Is>
struct recursive_reduction : std::type_identity<O> {};
template <template <class, class> class R, class O, class... Is>
using recursive_reduction_t = typename recursive_reduction<R, O, Is...>::type;
template <template <class, class> class R, class O, class I, class... Is>
struct recursive_reduction<R, O, I, Is...>
{ using type = recursive_reduction_t<R, R<O, I>, Is...>; };
: recursive_reduction<R, R<O, I>, Is...> {};
template <template <class, class> class R, class O, class... Is>
using recursive_reduction_t = typename recursive_reduction<R, O, Is...>::type;

template <class Expr>
consteval bool is_consteval(Expr)
Expand All @@ -106,7 +106,7 @@ struct instantiated_traits;
template <template <class...> class T, class TL, std::size_t... Is,
class... Args>
struct instantiated_traits<T, TL, std::index_sequence<Is...>, Args...>
{ using type = T<Args..., std::tuple_element_t<Is, TL>...>; };
: std::type_identity<T<Args..., std::tuple_element_t<Is, TL>...>> {};
template <template <class...> class T, class TL, class... Args>
using instantiated_t = typename instantiated_traits<
T, TL, std::make_index_sequence<std::tuple_size_v<TL>>, Args...>::type;
Expand Down Expand Up @@ -268,6 +268,7 @@ struct overload_traits_impl : applicable_traits {
}
};
using return_type = R;
using view_type = R(Args...) const noexcept(NE);

template <bool IS_DIRECT, class D, class P>
static constexpr bool applicable_ptr =
Expand Down Expand Up @@ -490,7 +491,7 @@ template <bool IS_DIRECT, class F, class... As, class I>
requires(IS_DIRECT == I::is_direct && !std::is_void_v<accessor_t<I, F>>)
struct composite_accessor_reduction<
IS_DIRECT, F, composite_accessor_impl<As...>, I>
{ using type = composite_accessor_impl<As..., accessor_t<I, F>>; };
: std::type_identity<composite_accessor_impl<As..., accessor_t<I, F>>> {};
template <bool IS_DIRECT, class F>
struct composite_accessor_helper {
template <class O, class I>
Expand Down Expand Up @@ -1165,6 +1166,12 @@ proxy<F> make_proxy(T&& value) {
}
#endif // __STDC_HOSTED__

template <class F>
struct observer_facade;

template <class F>
using proxy_view = proxy<observer_facade<F>>;

#define ___PRO_DIRECT_FUNC_IMPL(...) \
noexcept(noexcept(__VA_ARGS__)) requires(requires { __VA_ARGS__; }) \
{ return __VA_ARGS__; }
Expand Down Expand Up @@ -1366,20 +1373,20 @@ using merge_tuple_t = instantiated_t<merge_tuple_impl_t, U, T>;
template <bool IS_DIRECT, class D>
struct merge_conv_traits
{ template <class... Os> using type = conv_impl<IS_DIRECT, D, Os...>; };
template <class C0, class C1>
template <class C1, class C2>
using merge_conv_t = instantiated_t<
merge_conv_traits<C0::is_direct, typename C0::dispatch_type>::template type,
merge_tuple_t<typename C0::overload_types, typename C1::overload_types>>;

template <class Cs0, class C1, class C> struct add_conv_reduction;
template <class... Cs0, class C1, class... Cs2, class C>
struct add_conv_reduction<std::tuple<Cs0...>, std::tuple<C1, Cs2...>, C>
: add_conv_reduction<std::tuple<Cs0..., C1>, std::tuple<Cs2...>, C> {};
template <class... Cs0, class C1, class... Cs2, class C>
requires(C::is_direct == C1::is_direct && std::is_same_v<
typename C::dispatch_type, typename C1::dispatch_type>)
struct add_conv_reduction<std::tuple<Cs0...>, std::tuple<C1, Cs2...>, C>
: std::type_identity<std::tuple<Cs0..., merge_conv_t<C1, C>, Cs2...>> {};
merge_conv_traits<C1::is_direct, typename C1::dispatch_type>::template type,
merge_tuple_t<typename C1::overload_types, typename C2::overload_types>>;

template <class Cs1, class C2, class C> struct add_conv_reduction;
template <class... Cs1, class C2, class... Cs3, class C>
struct add_conv_reduction<std::tuple<Cs1...>, std::tuple<C2, Cs3...>, C>
: add_conv_reduction<std::tuple<Cs1..., C2>, std::tuple<Cs3...>, C> {};
template <class... Cs1, class C2, class... Cs3, class C>
requires(C::is_direct == C2::is_direct && std::is_same_v<
typename C::dispatch_type, typename C2::dispatch_type>)
struct add_conv_reduction<std::tuple<Cs1...>, std::tuple<C2, Cs3...>, C>
: std::type_identity<std::tuple<Cs1..., merge_conv_t<C2, C>, Cs3...>> {};
template <class... Cs, class C>
struct add_conv_reduction<std::tuple<Cs...>, std::tuple<>, C>
: std::type_identity<std::tuple<Cs..., merge_conv_t<
Expand Down Expand Up @@ -1411,8 +1418,8 @@ struct add_upward_conversion_conv<
Cs, F, constraint_level::none, constraint_level::none>
: std::type_identity<Cs> {};

template <class Cs0, class... Cs1>
using merge_conv_tuple_t = recursive_reduction_t<add_conv_t, Cs0, Cs1...>;
template <class Cs1, class... Cs2>
using merge_conv_tuple_t = recursive_reduction_t<add_conv_t, Cs1, Cs2...>;
template <class Cs, class F, bool WithUpwardConversion>
using merge_facade_conv_t = typename add_upward_conversion_conv<
instantiated_t<merge_conv_tuple_t, typename F::convention_types, Cs>, F,
Expand All @@ -1421,6 +1428,105 @@ using merge_facade_conv_t = typename add_upward_conversion_conv<
F::constraints.copyability != constraint_level::trivial) ?
F::constraints.relocatability : constraint_level::none>::type;

struct proxy_view_dispatch : cast_dispatch_base<false, true> {
template <class T>
auto* operator()(T&& value)
___PRO_DIRECT_FUNC_IMPL(std::addressof(*std::forward<T>(value)))
};

template <class P> struct facade_of_traits;
template <class F>
struct facade_of_traits<proxy<F>> : std::type_identity<F> {};
template <class P> using facade_of_t = typename facade_of_traits<P>::type;

template <class F, bool IS_DIRECT, class D, class O>
struct observer_overload_mapping_traits_impl
: std::type_identity<typename overload_traits<O>::view_type> {};
template <class F, class D, class O>
requires(!std::is_same_v<D, proxy_view_dispatch> ||
std::is_same_v<typename overload_traits<O>::return_type, proxy_view<F>>)
struct observer_overload_mapping_traits_impl<F, true, D, O>
: std::type_identity<void> {};
template <class F, bool IS_DIRECT, class D, class O>
struct observer_overload_mapping_traits : std::type_identity<void> {};
template <class F, bool IS_DIRECT, class D, class O>
requires(overload_traits<O>::qualifier ==
(std::is_const_v<F> ? qualifier_type::const_lv : qualifier_type::lv))
struct observer_overload_mapping_traits<F, IS_DIRECT, D, O>
: observer_overload_mapping_traits_impl<F, IS_DIRECT, D, O> {};
template <class F, class O>
struct observer_overload_mapping_traits<F, true, upward_conversion_dispatch, O>
: std::type_identity<proxy_view<
std::conditional_t<std::is_const_v<F>,
const facade_of_t<typename overload_traits<O>::return_type>,
facade_of_t<typename overload_traits<O>::return_type>
>>() const noexcept> {};

template <class D>
struct observer_dispatch_reduction : std::type_identity<D> {};
template <>
struct observer_dispatch_reduction<upward_conversion_dispatch>
: std::type_identity<proxy_view_dispatch> {};

template <class O, class I>
struct observer_overload_ignore_void_reduction : std::type_identity<O> {};
template <bool IS_DIRECT, class D, class... Os, class O>
requires(!std::is_void_v<O>)
struct observer_overload_ignore_void_reduction<
conv_impl<IS_DIRECT, D, Os...>, O>
: std::type_identity<conv_impl<IS_DIRECT, D, Os..., O>> {};
template <class O, class I>
using observer_overload_ignore_void_reduction_t =
typename observer_overload_ignore_void_reduction<O, I>::type;

template <class F, class C, class... Os>
using observer_conv_impl = recursive_reduction_t<
observer_overload_ignore_void_reduction_t,
conv_impl<C::is_direct, typename observer_dispatch_reduction<
typename C::dispatch_type>::type>,
typename observer_overload_mapping_traits<
F, C::is_direct, typename C::dispatch_type, Os>::type...>;

template <class O, class I>
struct observer_conv_reduction : std::type_identity<O> {};
template <class O, class I>
requires(std::tuple_size_v<typename I::overload_types> != 0u)
struct observer_conv_reduction<O, I> : std::type_identity<add_conv_t<O, I>> {};
template <class F>
struct observer_conv_reduction_traits {
template <class O, class I>
using type = typename observer_conv_reduction<O, instantiated_t<
observer_conv_impl, typename I::overload_types, F, I>>::type;
};

template <class O, class I>
struct observer_refl_reduction : std::type_identity<O> {};
template <class... Rs, class R> requires(!R::is_direct)
struct observer_refl_reduction<std::tuple<Rs...>, R>
: std::type_identity<std::tuple<Rs..., R>> {};
template <class O, class I>
using observer_refl_reduction_t = typename observer_refl_reduction<O, I>::type;

template <class F, class... Cs>
struct observer_facade_conv_impl {
using convention_types = recursive_reduction_t<
observer_conv_reduction_traits<F>::template type, std::tuple<>, Cs...>;
};
template <class... Rs>
struct observer_facade_refl_impl {
using reflection_types = recursive_reduction_t<
observer_refl_reduction_t, std::tuple<>, Rs...>;
};

template <class F>
struct proxy_view_overload_traits
: std::type_identity<proxy_view<F>() noexcept> {};
template <class F>
struct proxy_view_overload_traits<const F>
: std::type_identity<proxy_view<const F>() const noexcept> {};
template <class F>
using proxy_view_overload = typename proxy_view_overload_traits<F>::type;

template <std::size_t N>
struct sign {
consteval sign(const char (&str)[N])
Expand Down Expand Up @@ -1476,10 +1582,26 @@ struct basic_facade_builder {
template <constraint_level CL>
using support_destruction = basic_facade_builder<
Cs, Rs, details::make_destructible(C, CL)>;
template <class F>
using add_view = add_direct_convention<
details::proxy_view_dispatch, details::proxy_view_overload<F>>;
using build = details::facade_impl<Cs, Rs, details::normalize(C)>;
basic_facade_builder() = delete;
};

template <class F>
struct observer_facade
: details::instantiated_t<details::observer_facade_conv_impl,
typename F::convention_types, F>,
details::instantiated_t<details::observer_facade_refl_impl,
typename F::reflection_types> {
static constexpr proxiable_ptr_constraints constraints{
.max_size = sizeof(void*), .max_align = alignof(void*),
.copyability = constraint_level::trivial,
.relocatability = constraint_level::trivial,
.destructibility = constraint_level::trivial};
};

using facade_builder = basic_facade_builder<std::tuple<>, std::tuple<>,
proxiable_ptr_constraints{
.max_size = details::invalid_size,
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ add_executable(msft_proxy_tests
proxy_reflection_tests.cpp
proxy_regression_tests.cpp
proxy_traits_tests.cpp
proxy_view_tests.cpp
)
target_include_directories(msft_proxy_tests PRIVATE .)
target_link_libraries(msft_proxy_tests PRIVATE msft_proxy)
Expand Down
Loading