From 9fed4c48b01badd08d8ef0393138592eac8d25e9 Mon Sep 17 00:00:00 2001 From: "Jonathan B. Coe" Date: Sat, 25 Jul 2026 16:18:05 +0000 Subject: [PATCH] Add types.hxx: synthesizing member and vtable-entry function types parameter_types_of, member_function_type, and vtable_entry_pointer_type build a member's parameter list, call signature, and erased vtable-entry pointer type via std::meta::substitute. --- CMakeLists.txt | 4 +- protocol_reflection_detail/types.hxx | 75 ++++++++++++++++++++++ protocol_reflection_detail/types_test.cc | 81 ++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 protocol_reflection_detail/types.hxx create mode 100644 protocol_reflection_detail/types_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 8713751..5d32094 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,7 +214,9 @@ if(XYZ_PROTOCOL_IS_NOT_SUBPROJECT) protocol_reflection_detail/naming.hxx protocol_reflection_detail/naming_test.cc protocol_reflection_detail/members.hxx - protocol_reflection_detail/members_test.cc) + protocol_reflection_detail/members_test.cc + protocol_reflection_detail/types.hxx + protocol_reflection_detail/types_test.cc) target_compile_options(protocol_reflection_detail_test PRIVATE -freflection) target_include_directories(protocol_reflection_detail_test diff --git a/protocol_reflection_detail/types.hxx b/protocol_reflection_detail/types.hxx new file mode 100644 index 0000000..08ddb06 --- /dev/null +++ b/protocol_reflection_detail/types.hxx @@ -0,0 +1,75 @@ +/* Copyright (c) 2025 The XYZ Protocol Authors. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==============================================================================*/ +// Synthesizing member and vtable-entry function types from a reflected +// member function, via std::meta::substitute over a variadic alias +// template. +#ifndef XYZ_PROTOCOL_REFLECTION_DETAIL_TYPES_HXX_ +#define XYZ_PROTOCOL_REFLECTION_DETAIL_TYPES_HXX_ + +#include +#include + +namespace xyz::reflection_detail { + +// `member`'s parameter types, dealiased. std::meta::parameters_of never +// yields the implicit object parameter, so none of these are it. +consteval std::vector parameter_types_of( + std::meta::info member) { + std::vector types; + for (std::meta::info parameter : std::meta::parameters_of(member)) { + types.push_back(std::meta::dealias(std::meta::type_of(parameter))); + } + return types; +} + +template +using function_type_t = R(Ps...); + +// `member`'s own call signature as an ordinary function type, R(Ps...). +consteval std::meta::info member_function_type(std::meta::info member) { + std::vector args{ + std::meta::dealias(std::meta::return_type_of(member))}; + for (std::meta::info parameter_type : parameter_types_of(member)) { + args.push_back(parameter_type); + } + return std::meta::substitute(^^function_type_t, args); +} + +template +using fn_ptr_t = R (*)(Ps...); + +// The vtable entry's pointer-to-function type for `member`: +// R (*)(ErasedPointer, Ps...), with the member's implicit object parameter +// replaced by `erased_pointer_type` (typically ^^void* for a non-const +// member, ^^const void* for a const one). +consteval std::meta::info vtable_entry_pointer_type( + std::meta::info member, std::meta::info erased_pointer_type) { + std::vector args{ + std::meta::dealias(std::meta::return_type_of(member)), + erased_pointer_type}; + for (std::meta::info parameter_type : parameter_types_of(member)) { + args.push_back(parameter_type); + } + return std::meta::substitute(^^fn_ptr_t, args); +} + +} // namespace xyz::reflection_detail + +#endif // XYZ_PROTOCOL_REFLECTION_DETAIL_TYPES_HXX_ diff --git a/protocol_reflection_detail/types_test.cc b/protocol_reflection_detail/types_test.cc new file mode 100644 index 0000000..78dde5e --- /dev/null +++ b/protocol_reflection_detail/types_test.cc @@ -0,0 +1,81 @@ +/* Copyright (c) 2025 The XYZ Protocol Authors. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==============================================================================*/ +#include "protocol_reflection_detail/types.hxx" + +#include + +#include +#include + +namespace { + +using xyz::reflection_detail::member_function_type; +using xyz::reflection_detail::parameter_types_of; +using xyz::reflection_detail::vtable_entry_pointer_type; + +struct Fixture { + int compute(double x, int y) const { return static_cast(x) + y; } + + void set_value(int) {} + + void no_args() {} +}; + +TEST(ParameterTypesOf, ListsEachParameterInOrder) { + constexpr auto types = + std::define_static_array(parameter_types_of(^^Fixture::compute)); + static_assert(types.size() == 2); + static_assert(types[0] == ^^double); + static_assert(types[1] == ^^int); +} + +TEST(ParameterTypesOf, EmptyForANoArgumentMember) { + constexpr auto types = + std::define_static_array(parameter_types_of(^^Fixture::no_args)); + static_assert(types.size() == 0); +} + +TEST(MemberFunctionType, BuildsTheMembersOwnCallSignature) { + static_assert( + std::is_same_v); + static_assert( + std::is_same_v< + typename[:member_function_type(^^Fixture::set_value):], void(int)>); + static_assert(std::is_same_v< + typename[:member_function_type(^^Fixture::no_args):], void()>); +} + +TEST(VtableEntryPointerType, + ReplacesTheImplicitObjectParameterWithAnErasedPointer) { + static_assert( + std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v< + typename[:vtable_entry_pointer_type( + ^^Fixture::no_args, ^^void*):], void (*)(void*)>); +} + +} // namespace