include/ylt/reflection/member_names.hpp (316 lines of code) (raw):

#pragma once #include <string_view> #include <variant> #include "member_ptr.hpp" #include "template_string.hpp" namespace ylt::reflection { template <typename T> inline constexpr auto get_alias_field_names(); namespace internal { template <class T> struct Wrapper { using Type = T; T v; }; template <class T> Wrapper(T) -> Wrapper<T>; // This workaround is necessary for clang. template <class T> inline constexpr auto wrap(const T& arg) noexcept { return Wrapper{arg}; } template <auto ptr> inline constexpr std::string_view get_member_name() { #if defined(_MSC_VER) constexpr std::string_view func_name = __FUNCSIG__; #else constexpr std::string_view func_name = __PRETTY_FUNCTION__; #endif #if defined(__clang__) auto split = func_name.substr(0, func_name.size() - 2); return split.substr(split.find_last_of(":.") + 1); #elif defined(__GNUC__) auto split = func_name.substr(0, func_name.rfind(")}")); return split.substr(split.find_last_of(":") + 1); #elif defined(_MSC_VER) auto split = func_name.substr(0, func_name.rfind("}>")); return split.substr(split.rfind("->") + 2); #else static_assert(false, "You are using an unsupported compiler. Please use GCC, Clang " "or MSVC or switch to the rfl::Field-syntax."); #endif } template <class T> struct member_tratis {}; template <class T, class Owner> struct member_tratis<T Owner::*> { using owner_type = Owner; using value_type = T; }; template <typename T, typename = void> struct has_alias_struct_name_t : std::false_type {}; template <typename T> struct has_alias_struct_name_t< T, std::void_t<decltype(get_alias_struct_name((T*)nullptr))>> : std::true_type {}; template <typename T, typename = void> struct has_inner_alias_struct_name_t : std::false_type {}; template <typename T> struct has_inner_alias_struct_name_t< T, std::void_t<decltype(T::get_alias_struct_name((T*)nullptr))>> : std::true_type {}; template <typename T> constexpr bool has_alias_struct_name_v = has_alias_struct_name_t<T>::value; template <typename T> constexpr bool has_inner_alias_struct_name_v = has_inner_alias_struct_name_t<T>::value; template <typename T, typename = void> struct has_alias_field_names_t : std::false_type {}; template <typename T> struct has_alias_field_names_t< T, std::void_t<decltype(get_alias_field_names((T*)nullptr))>> : std::true_type {}; template <typename T, typename = void> struct has_inner_alias_field_names_t : std::false_type {}; template <typename T> struct has_inner_alias_field_names_t< T, std::void_t<decltype(T::get_alias_field_names((T*)nullptr))>> : std::true_type {}; template <typename T> constexpr bool has_alias_field_names_v = has_alias_field_names_t<T>::value; template <typename T> constexpr bool has_inner_alias_field_names_v = has_inner_alias_field_names_t<T>::value; template <typename T, typename U, size_t... Is> inline constexpr void init_arr_with_tuple(U& arr, std::index_sequence<Is...>) { constexpr auto tp = struct_to_tuple<T>(); ((arr[Is] = internal::get_member_name<internal::wrap(std::get<Is>(tp))>()), ...); } template <typename T> inline constexpr std::array<std::string_view, members_count_v<T>> get_member_names() { constexpr size_t Count = members_count_v<T>; using type = remove_cvref_t<T>; if constexpr (is_out_ylt_refl_v<type>) { return refl_member_names(ylt::reflection::identity<type>{}); } else if constexpr (is_inner_ylt_refl_v<type>) { return type::refl_member_names(ylt::reflection::identity<type>{}); } else { std::array<std::string_view, Count> arr; #if __cplusplus >= 202002L && (!defined(_MSC_VER) || _MSC_VER >= 1930) constexpr auto tp = struct_to_tuple<T>(); [&]<size_t... Is>(std::index_sequence<Is...>) mutable { ((arr[Is] = internal::get_member_name<internal::wrap(std::get<Is>(tp))>()), ...); } (std::make_index_sequence<Count>{}); #else init_arr_with_tuple<T>(arr, std::make_index_sequence<Count>{}); #endif return arr; } } template <typename T, size_t... Is> inline constexpr auto get_member_names_map_impl(T& name_arr, std::index_sequence<Is...>) { return frozen::unordered_map<frozen::string, size_t, sizeof...(Is)>{ {name_arr[Is], Is}...}; } template <typename T> inline constexpr auto get_member_names_map() { constexpr auto name_arr = get_member_names<T>(); #if __cplusplus >= 202002L return [&]<size_t... Is>(std::index_sequence<Is...>) mutable { return frozen::unordered_map<frozen::string, size_t, name_arr.size()>{ {name_arr[Is], Is}...}; } (std::make_index_sequence<name_arr.size()>{}); #else return get_member_names_map_impl(name_arr, std::make_index_sequence<name_arr.size()>{}); #endif } template <typename T, typename Tuple, size_t... Is> inline auto get_member_offset_arr_impl(T& t, Tuple& tp, std::index_sequence<Is...>) { std::array<size_t, sizeof...(Is)> arr; ((arr[Is] = size_t((const char*)&std::get<Is>(tp) - (char*)(&t))), ...); return arr; } template <typename T> inline const auto& get_member_offset_arr(T&& t) { constexpr size_t Count = members_count_v<T>; auto tp = ylt::reflection::object_to_tuple(std::forward<T>(t)); #if __cplusplus >= 202002L [[maybe_unused]] static std::array<size_t, Count> arr = {[&]<size_t... Is>( std::index_sequence<Is...>) mutable {std::array<size_t, Count> arr; ((arr[Is] = size_t((const char*)&std::get<Is>(tp) - (char*)(&t))), ...); return arr; } (std::make_index_sequence<Count>{}) }; // namespace internal return arr; #else [[maybe_unused]] static std::array<size_t, Count> arr = get_member_offset_arr_impl(t, tp, std::make_index_sequence<Count>{}); return arr; #endif } // namespace ylt::reflection template <typename T> inline const auto& get_member_offset_arr() { return get_member_offset_arr(internal::wrapper<T>::value); } // namespace ylt::reflection } // namespace internal template <typename... Args> inline constexpr auto tuple_to_variant(std::tuple<Args...>) { return std::variant<std::add_pointer_t<Args>...>{}; } template <typename T> using struct_variant_t = decltype(tuple_to_variant( std::declval<decltype(struct_to_tuple<remove_cvref_t<T>>())>)); template <typename T> constexpr auto member_names_map = internal::get_member_names_map<T>(); template <typename T> inline auto member_offsets = internal::get_member_offset_arr<T>(); template <auto member> inline constexpr size_t index_of() { using T = typename internal::member_tratis< std::remove_const_t<decltype(member)>>::owner_type; constexpr auto name = field_string<member>(); constexpr auto names = internal::get_member_names<T>(); for (size_t i = 0; i < names.size(); i++) { if (name == names[i]) { return i; } } return names.size(); } struct field_alias_t { std::string_view alias_name; size_t index; }; template <typename T> inline constexpr auto get_alias_field_names() { if constexpr (internal::has_alias_field_names_v<T>) { return get_alias_field_names((T*)nullptr); } else if constexpr (internal::has_inner_alias_field_names_v<T>) { return T::get_alias_field_names((T*)nullptr); } else { return std::array<std::string_view, 0>{}; } } template <typename T> constexpr std::string_view get_struct_name() { if constexpr (internal::has_alias_struct_name_v<T>) { return get_alias_struct_name((T*)nullptr); } else if constexpr (internal::has_inner_alias_struct_name_v<T>) { return T::get_alias_struct_name((T*)nullptr); } else { return type_string<T>(); } } template <typename T> inline constexpr std::array<std::string_view, members_count_v<T>> get_member_names() { auto arr = internal::get_member_names<T>(); using U = ylt::reflection::remove_cvref_t<T>; if constexpr (internal::has_alias_field_names_v<U> || internal::has_inner_alias_field_names_v<U>) { constexpr auto alias_arr = get_alias_field_names<U>(); for (size_t i = 0; i < alias_arr.size(); i++) { arr[alias_arr[i].index] = alias_arr[i].alias_name; } } return arr; } template <std::size_t N> struct FixedString { char data[N]; template <std::size_t... I> constexpr FixedString(const char (&s)[N], std::index_sequence<I...>) : data{s[I]...} {} constexpr FixedString(const char (&s)[N]) : FixedString(s, std::make_index_sequence<N>()) {} constexpr std::string_view str() const { return std::string_view{data, N - 1}; } }; template <typename T> inline constexpr size_t index_of(std::string_view name) { constexpr auto arr = get_member_names<T>(); for (size_t i = 0; i < arr.size(); i++) { if (arr[i] == name) { return i; } } return arr.size(); } #if __cplusplus >= 202002L template <typename T, FixedString name> inline constexpr size_t index_of() { return index_of<T>(name.str()); } #endif template <typename T, size_t index> inline constexpr std::string_view name_of() { static_assert(index < members_count_v<T>, "index out of range"); constexpr auto arr = get_member_names<T>(); return arr[index]; } template <typename T> inline constexpr std::string_view name_of(size_t index) { constexpr auto arr = get_member_names<T>(); if (index >= arr.size()) { return ""; } return arr[index]; } template <typename Visit, typename U, size_t... Is> inline constexpr void for_each_impl(Visit&& func, U& arr, std::index_sequence<Is...>) { if constexpr (std::is_invocable_v<Visit, std::string_view, size_t>) { (func(arr[Is], Is), ...); } else if constexpr (std::is_invocable_v<Visit, std::string_view>) { (func(arr[Is]), ...); } else { static_assert(sizeof(Visit) < 0, "invalid arguments, full arguments: [std::string_view, " "size_t], at least has std::string_view and make sure keep " "the order of arguments"); } } template <typename T, typename Visit> inline constexpr void for_each(Visit&& func) { constexpr auto arr = get_member_names<T>(); #if __cplusplus >= 202002L [&]<size_t... Is>(std::index_sequence<Is...>) mutable { if constexpr (std::is_invocable_v<Visit, std::string_view, size_t>) { (func(arr[Is], Is), ...); } else if constexpr (std::is_invocable_v<Visit, std::string_view>) { (func(arr[Is]), ...); } else { static_assert(sizeof(Visit) < 0, "invalid arguments, full arguments: [std::string_view, " "size_t], at least has std::string_view and make sure keep " "the order of arguments"); } } (std::make_index_sequence<arr.size()>{}); #else for_each_impl(std::forward<Visit>(func), arr, std::make_index_sequence<arr.size()>{}); #endif } } // namespace ylt::reflection