From bb4fa771bb263e0e592d1e029f2235d5060c97df Mon Sep 17 00:00:00 2001 From: Nichts Hsu Date: Tue, 20 Feb 2024 23:12:24 +0800 Subject: [PATCH] feat: add more reflect APIs 1. add `index_of`, can get member index via its name. 2. add `type_of_member`, can get member type via its name. 3. add `member_of`, can get reference to member via its index or name. --- include/conststr.hpp | 18 +++++++ include/reflect.hpp | 106 ++++++++++++++++++++++++++++++++++++++++- tests/test-reflect.cpp | 41 ++++++++++++++++ 3 files changed, 163 insertions(+), 2 deletions(-) diff --git a/include/conststr.hpp b/include/conststr.hpp index d081173..a7746d0 100644 --- a/include/conststr.hpp +++ b/include/conststr.hpp @@ -189,15 +189,33 @@ * }; * * int main() { + * // Get member type of `MyStruct` via index * reflect::type_of a = 1; // type of `a` is `int` * reflect::type_of b = 1.f; // type of `b` is `double` * reflect::type_of c = "hello"; // type of `c` is `std::string` * + * // Also you can get member type via its name + * reflect::type_of_member d = 1; + * reflect::type_of_member e = 1.f; + * reflect::type_of_member f = "hello"; + * + * // Get member name via index * std::cout << reflect::number_of_members << std::endl; // 3 members in `MyStruct` * std::cout << reflect::name_of << std::endl; // first member is "number" * std::cout << reflect::name_of << std::endl; // second member is "decimal" * std::cout << reflect::name_of << std::endl; // third member is "name" * + * // Get member reference via index ... + * MyStruct s; + * decltype(auto) numref = reflect::member_of<0>(s); + * numref = 100; + * std::cout << s.number << std::endl; + * + * // ...or, via its name + * decltype(auto) nameref = reflect::member_of<"name">(s); + * nameref += "hello world"; + * std::cout << s.name << std::endl; + * * return 0; * } * @endcode diff --git a/include/reflect.hpp b/include/reflect.hpp index 7b5b1f4..97a25ff 100644 --- a/include/reflect.hpp +++ b/include/reflect.hpp @@ -1771,8 +1771,8 @@ consteval auto cptr_of_member() { * @note * When using MSVC, will return a string in the form of * `"auto __cdecl reflect::pretty_name<...>(void)"`. - * @tparam Ptr pointer to the the variable or field you want to reflect - * @return A compile-time string containing the name of the variable or field + * @tparam Ptr pointer to the the variable or member you want to reflect + * @return A compile-time string containing the name of the variable or member * and of type `conststr::cstr`. * @see conststr::cstr * @see name_of() @@ -1866,6 +1866,108 @@ constexpr auto name_of_ptr = name_of_ptr_impl(); template constexpr auto name_of = name_of_ptr()>; #endif + +/** + * @brief Internal implementation of `index_of`. + * Get the index of the member by its name. + * @tparam T any default-constructible aggregate type + * @tparam Name name of the member to search + * @tparam SrchIdx for recursion only, keep it 0 + * @return Index of the member. + * @see index_of + */ +template +constexpr size_t index_of_impl() + requires(number_of_members > SrchIdx) +{ + if constexpr (Name == name_of) + return SrchIdx; + else + return index_of_impl(); +} + +/** + * @brief Get the index of the member by its name. + * @details + * For example, `reflect::index_of` if the type `S` has a member named `point`. + * @tparam T any default-constructible aggregate type + * @tparam Name name of the member to search + * @return Index of the member. + */ +template +constexpr size_t index_of = index_of_impl(); + +/** + * @brief Get the type of the member by its name. + * @details + * This is just a simplified API of `reflect::type_of>`. + * @tparam T any default-constructible aggregate type + * @tparam Name name of the member to search + */ +template +using type_of_member = type_of>; + +/** + * @brief Get member reference of object `t`. + * @details + * Please use `decltype(auto)` for deducing the return value type. + * For example: + * @code{.cpp} + * struct S { + * int x; + * int y; + * int z; + * }; + * S s = {}; + * decltype(auto) zref = reflect::member_of<2>(s); + * zref = 10; + * if (s.z == 10) + * std::cout << "Ok" << std::endl; + * @endcode + * @tparam N index of member + * @tparam T DO NOT specify it, let it be automatically deduced + * @param t object of type `T` + * @return L-value reference if `t` is a l-value reference. + * @return R-value reference if `t` is a r-value reference. + */ +template +constexpr decltype(auto) member_of(T &&t) { + if constexpr (std::is_lvalue_reference_v) + return std::get(to_tuple(std::forward(t))); + else + return std::move(std::get(to_tuple(std::forward(t)))); +} + +/** + * @brief Get member reference of object `t` via member's name. + * @details + * Please use `decltype(auto)` for deducing the return value type. + * For example: + * @code{.cpp} + * struct S { + * int x; + * int y; + * int z; + * }; + * S s = {}; + * decltype(auto) zref = reflect::member_of<"z">(s); + * zref = 10; + * if (s.z == 10) + * std::cout << "Ok" << std::endl; + * @endcode + * @tparam Name name of member + * @tparam T DO NOT specify it, let it be automatically deduced + * @param t object of type `T` + * @return L-value reference if `t` is a l-value reference. + * @return R-value reference if `t` is a r-value reference. + */ +template +constexpr decltype(auto) member_of(T &&t) + requires std::same_as +{ + return member_of, Name>, T>( + std::forward(t)); +} } // namespace reflect #endif \ No newline at end of file diff --git a/tests/test-reflect.cpp b/tests/test-reflect.cpp index d39bb44..592fa5a 100644 --- a/tests/test-reflect.cpp +++ b/tests/test-reflect.cpp @@ -53,6 +53,47 @@ int main() { static_assert(reflect::name_of == "uncopyable"); static_assert(reflect::name_of == "ref_wrapper"); + static_assert( + std::same_as, int>); + static_assert( + std::same_as, double>); + static_assert( + std::same_as, std::string>); + static_assert(std::same_as, + std::size_t[16]>); + static_assert( + std::same_as, void *>); + static_assert( + std::same_as, + int (*)(int)>); + static_assert( + std::same_as, + int helper::*>); + static_assert( + std::same_as, + int (helper::*)(int)>); + static_assert(std::same_as, + std::unique_ptr>); + static_assert(std::same_as, + std::reference_wrapper>); + + MyStruct s; + decltype(auto) member0 = reflect::member_of<0>(s); + member0 = 114; + decltype(auto) member0_ = reflect::member_of<0>(std::move(s)); + static_assert(std::is_lvalue_reference_v); + static_assert(std::is_rvalue_reference_v); + if (s.number != 114) return 1; + decltype(auto) member3 = reflect::member_of<3>(s); + member3[10] = 514; + if (s.array[10] != 514) return 1; + decltype(auto) member4 = reflect::member_of<"pointer">(s); + decltype(auto) member4_ = reflect::member_of<"pointer">(std::move(s)); + static_assert(std::is_lvalue_reference_v); + static_assert(std::is_rvalue_reference_v); + member4 = (void *)0x1919810; + if (s.pointer != (void *)0x1919810) return 1; + std::cout << __FILE__ ": all tests passed." << std::endl; return 0;