include/ylt/struct_pack/reflection.hpp (750 lines of code) (raw):

/* * Copyright (c) 2023, Alibaba Group Holding Limited; * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // clang-format off #pragma once #include <cstddef> #include <cstdint> #include <cstring> #include <limits> #include <set> #include <string> #include <string_view> #include <tuple> #include <type_traits> #include <utility> #include <variant> #include <vector> #include "ylt/reflection/member_count.hpp" #include "ylt/reflection/internal/arg_list_macro.hpp" #if __has_include(<span>) #include <span> #endif #include "derived_helper.hpp" #include "foreach_macro.h" #include "marco.h" #include "util.h" #include "ylt/reflection/template_switch.hpp" #include "ylt/reflection/member_ptr.hpp" #if __cpp_concepts >= 201907L #include <concepts> #endif namespace struct_pack { enum sp_config : uint64_t { DEFAULT = 0, DISABLE_TYPE_INFO = 0b1, ENABLE_TYPE_INFO = 0b10, DISABLE_ALL_META_INFO = 0b11, ENCODING_WITH_VARINT = 0b100, USE_FAST_VARINT = 0b1000 }; namespace detail { template <typename... Args> using get_args_type = remove_cvref_t<typename std::conditional< sizeof...(Args) == 1, std::tuple_element_t<0, std::tuple<Args...>>, std::tuple<Args...>>::type>; template <typename U> constexpr auto get_types(); template <typename T, template <typename, typename, std::size_t> typename Op, typename... Contexts, std::size_t... I> constexpr void for_each_impl(std::index_sequence<I...>, Contexts &...contexts) { using type = decltype(get_types<T>()); (Op<T, std::tuple_element_t<I, type>, I>{}(contexts...), ...); } template <typename T, template <typename, typename, std::size_t> typename Op, typename... Contexts> constexpr void for_each(Contexts &...contexts) { using type = decltype(get_types<T>()); for_each_impl<T, Op>(std::make_index_sequence<std::tuple_size_v<type>>(), contexts...); } template <typename T> constexpr std::size_t members_count(); template <typename T> constexpr std::size_t pack_align(); template <typename T> constexpr std::size_t alignment(); } // namespace detail template <typename T> constexpr std::size_t members_count = detail::members_count<T>(); template <typename T> constexpr std::size_t pack_alignment_v = 0; template <typename T> constexpr std::size_t alignment_v = 0; #if __cpp_concepts >= 201907L template <typename T> concept writer_t = requires(T t) { t.write((const char *)nullptr, std::size_t{}); }; template <typename T> concept reader_t = requires(T t) { t.read((char *)nullptr, std::size_t{}); t.ignore(std::size_t{}); t.tellg(); }; template <typename T> concept view_reader_t = reader_t<T> && requires(T t) { { t.read_view(std::size_t{}) } -> std::convertible_to<const char *>; }; #else template <typename T, typename = void> struct writer_t_impl : std::false_type {}; template <typename T> struct writer_t_impl<T, std::void_t<decltype(std::declval<T>().write( (const char *)nullptr, std::size_t{}))>> : std::true_type {}; template <typename T> constexpr bool writer_t = writer_t_impl<T>::value; template <typename T, typename = void> struct reader_t_impl : std::false_type {}; template <typename T> struct reader_t_impl< T, std::void_t<decltype(std::declval<T>().read((char *)nullptr, std::size_t{})), decltype(std::declval<T>().ignore(std::size_t{})), decltype(std::declval<T>().tellg())>> : std::true_type {}; template <typename T> constexpr bool reader_t = reader_t_impl<T>::value; template <typename T, typename = void> struct view_reader_t_impl : std::false_type {}; template <typename T> struct view_reader_t_impl< T, std::void_t<decltype(std::declval<T>().read_view(std::size_t{}))>> : std::true_type {}; template <typename T> constexpr bool view_reader_t = reader_t<T> &&view_reader_t_impl<T>::value; #endif #if __cpp_concepts >= 201907L template <typename T> concept can_reserve = requires(T t) { t.reserve(std::size_t{}); }; template <typename T> concept can_shrink_to_fit = requires(T t) { t.shrink_to_fit(); }; #else template <typename T, typename = void> struct can_reserve_impl : std::false_type {}; template <typename T> struct can_reserve_impl< T, std::void_t<decltype(std::declval<T>().reserve(std::size_t{}))>> : std::true_type {}; template <typename T> constexpr bool can_reserve = can_reserve_impl<T>::value; template <typename T, typename = void> struct can_shrink_to_fit_impl : std::false_type {}; template <typename T> struct can_shrink_to_fit_impl< T, std::void_t<decltype(std::declval<T>().shrink_to_fit())>> : std::true_type {}; template <typename T> constexpr bool can_shrink_to_fit = can_shrink_to_fit_impl<T>::value; #endif template <typename T, uint64_t version = 0> struct compatible; namespace detail { #if __cpp_concepts >= 201907L template <typename T> concept has_user_defined_id = requires { typename std::integral_constant<std::size_t, T::struct_pack_id>; }; template <typename T> concept has_user_defined_id_ADL = requires { typename std::integral_constant<std::size_t, struct_pack_id((T*)nullptr)>; }; #else template <typename T, typename = void> struct has_user_defined_id_impl : std::false_type {}; template <typename T> struct has_user_defined_id_impl< T, std::void_t<std::integral_constant<std::size_t, T::struct_pack_id>>> : std::true_type {}; template <typename T> constexpr bool has_user_defined_id = has_user_defined_id_impl<T>::value; template <std::size_t sz> struct constant_checker{}; template <typename T, typename = void> struct has_user_defined_id_ADL_impl : std::false_type {}; #ifdef _MSC_VER // FIXME: we can't check if it's compile-time calculated in msvc with C++17 template <typename T> struct has_user_defined_id_ADL_impl< T, std::void_t<decltype(struct_pack_id((T*)nullptr))>> : std::true_type {}; #else template <typename T> struct has_user_defined_id_ADL_impl< T, std::void_t<constant_checker<struct_pack_id((T*)nullptr)>>> : std::true_type {}; #endif template <typename T> constexpr bool has_user_defined_id_ADL = has_user_defined_id_ADL_impl<T>::value; #endif #if __cpp_concepts >= 201907L template <typename T> concept is_base_class = requires (T* t) { std::is_same_v<uint32_t,decltype(t->get_struct_pack_id())>; typename struct_pack::detail::derived_class_set_t<T>; }; #else template <typename T, typename = void> struct is_base_class_impl : std::false_type {}; template <typename T> struct is_base_class_impl< T, std::void_t< std::enable_if<std::is_same_v<decltype(((T*)nullptr)->get_struct_pack_id()), uint32_t>>, typename struct_pack::detail::derived_class_set_t<T>>> : std::true_type {}; template <typename T> constexpr bool is_base_class=is_base_class_impl<T>::value; #endif #if __cpp_concepts >= 201907L template <typename Type> concept deserialize_view = requires(Type container) { container.size(); container.data(); }; #else template <typename T, typename = void> struct deserialize_view_impl : std::false_type {}; template <typename T> struct deserialize_view_impl< T, std::void_t<decltype(std::declval<T>().size()),decltype(std::declval<T>().data())>> : std::true_type {}; template <typename Type> constexpr bool deserialize_view = deserialize_view_impl<Type>::value; #endif struct memory_writer { char *buffer; STRUCT_PACK_INLINE void write(const char *data, std::size_t len) { memcpy(buffer, data, len); buffer += len; } }; #if __cpp_concepts >= 201907L template <typename Type> concept container_adapter = requires(Type container) { typename remove_cvref_t<Type>::value_type; container.size(); container.pop(); }; #else template <typename T, typename = void> struct container_adapter_impl : std::false_type {}; template <typename T> struct container_adapter_impl<T, std::void_t< typename remove_cvref_t<T>::value_type, decltype(std::declval<T>().size()), decltype(std::declval<T>().pop())>> : std::true_type {}; template <typename T> constexpr bool container_adapter = container_adapter_impl<T>::value; #endif #if __cpp_concepts >= 201907L template <typename Type> concept container = requires(Type container) { typename remove_cvref_t<Type>::value_type; container.size(); container.begin(); container.end(); }; #else template <typename T, typename = void> struct container_impl : std::false_type {}; template <typename T> struct container_impl<T, std::void_t< typename remove_cvref_t<T>::value_type, decltype(std::declval<T>().size()), decltype(std::declval<T>().begin()), decltype(std::declval<T>().end())>> : std::true_type {}; template <typename T> constexpr bool container = container_impl<T>::value; #endif template <typename Type> constexpr bool is_char_t = std::is_same_v<Type, signed char> || std::is_same_v<Type, char> || std::is_same_v<Type, unsigned char> || std::is_same_v<Type, wchar_t> || std::is_same_v<Type, char16_t> || std::is_same_v<Type, char32_t> #ifdef __cpp_lib_char8_t || std::is_same_v<Type, char8_t> #endif ; #if __cpp_concepts >= 201907L template <typename Type> concept string = container<Type> && requires(Type container) { requires is_char_t<typename remove_cvref_t<Type>::value_type>; container.length(); container.data(); }; #else template <typename T, typename = void> struct string_impl : std::false_type {}; template <typename T> struct string_impl<T, std::void_t< std::enable_if_t<is_char_t<typename remove_cvref_t<T>::value_type>>, decltype(std::declval<T>().length()), decltype(std::declval<T>().data())>> : std::true_type {}; template <typename T> constexpr bool string = string_impl<T>::value && container<T>; #endif #if __cpp_concepts >= 201907L template <typename Type> concept string_view = string<Type> && !requires(Type container) { container.resize(std::size_t{}); }; #else template <typename T, typename = void> struct string_view_impl : std::true_type {}; template <typename T> struct string_view_impl<T, std::void_t< decltype(std::declval<T>().resize(std::size_t{}))>> : std::false_type {}; template <typename T> constexpr bool string_view = string<T> && string_view_impl<T>::value; #endif #if __cpp_concepts >= 201907L template <typename Type> concept span = container<Type> && requires(Type t) { Type{(typename Type::value_type*)nullptr ,std::size_t{} }; t.subspan(std::size_t{},std::size_t{}); }; #else template <typename T, typename = void> struct span_impl : std::false_type {}; template <typename T> struct span_impl<T, std::void_t< decltype(T{(typename T::value_type*)nullptr ,std::size_t{}}), decltype(std::declval<T>().subspan(std::size_t{},std::size_t{}))>> : std::true_type {}; template <typename T> constexpr bool span = container<T> && span_impl<T>::value; #endif #if __cpp_concepts >= 201907L template <typename Type> concept dynamic_span = span<Type> && Type::extent == SIZE_MAX; #else template <typename T, typename = void> struct dynamic_span_impl : std::false_type {}; template <typename T> struct dynamic_span_impl<T, std::void_t< std::enable_if_t<(T::extent == SIZE_MAX)>>> : std::true_type {}; template <typename T> constexpr bool dynamic_span = span<T> && dynamic_span_impl<T>::value; #endif template <typename Type> constexpr bool static_span = span<Type> && !dynamic_span<Type>; #if __cpp_lib_span >= 202002L && __cpp_concepts>=201907L template <typename Type> concept continuous_container = string<Type> || (container<Type> && requires(Type container) { std::span{container}; }); #else template <typename Type> constexpr inline bool is_std_basic_string_v = false; template <typename... args> constexpr inline bool is_std_basic_string_v<std::basic_string<args...>> = true; template <typename Type> constexpr inline bool is_std_vector_v = false; template <typename... args> constexpr inline bool is_std_vector_v<std::vector<args...>> = true; template <typename Type> constexpr bool continuous_container = string<Type> || (container<Type> && (is_std_vector_v<Type> || is_std_basic_string_v<Type>)); #endif #if __cpp_concepts >= 201907L template <typename Type> concept map_container = container<Type> && requires(Type container) { typename remove_cvref_t<Type>::mapped_type; }; #else template <typename T, typename = void> struct map_container_impl : std::false_type {}; template <typename T> struct map_container_impl<T, std::void_t< typename remove_cvref_t<T>::mapped_type>> : std::true_type {}; template <typename T> constexpr bool map_container = container<T> && map_container_impl<T>::value; #endif #if __cpp_concepts >= 201907L template <typename Type> concept set_container = container<Type> && requires { typename remove_cvref_t<Type>::key_type; }; #else template <typename T, typename = void> struct set_container_impl : std::false_type {}; template <typename T> struct set_container_impl<T, std::void_t< typename remove_cvref_t<T>::key_type>> : std::true_type {}; template <typename T> constexpr bool set_container = container<T> && set_container_impl<T>::value; #endif #if __cpp_concepts >= 201907L template <typename Type> concept bitset = requires (Type t){ t.flip(); t.set(); t.reset(); t.count(); } && (Type{}.size()+7)/8 == sizeof(Type); #else template <typename T, typename = void> struct bitset_impl : std::false_type {}; template <typename T> struct bitset_impl<T, std::void_t< decltype(std::declval<T>().flip()), decltype(std::declval<T>().set()), decltype(std::declval<T>().reset()), decltype(std::declval<T>().count()), decltype(std::declval<T>().size())>> : std::true_type {}; template<typename T> constexpr bool bitset_size_checker() { if constexpr (bitset_impl<T>::value) { return (T{}.size()+7)/8==sizeof(T); } else { return false; } } template <typename T> constexpr bool bitset = bitset_impl<T>::value && bitset_size_checker<T>(); #endif #if __cpp_concepts >= 201907L template <typename Type> concept tuple = requires(Type tuple) { std::get<0>(tuple); sizeof(std::tuple_size<remove_cvref_t<Type>>); }; #else template <typename T, typename = void> struct tuple_impl : std::false_type {}; template <typename T> struct tuple_impl<T, std::void_t< decltype(std::get<0>(std::declval<T>())), decltype(sizeof(std::tuple_size<remove_cvref_t<T>>::value))>> : std::true_type {}; template <typename T> constexpr bool tuple = tuple_impl<T>::value; #endif #if __cpp_concepts >= 201907L template <typename Type> concept user_defined_refl = ylt::reflection::is_out_ylt_refl_v<Type> || ylt::reflection::is_inner_ylt_refl_v<Type>; #else template <typename T> constexpr bool user_defined_refl = ylt::reflection::is_out_ylt_refl_v<T> || ylt::reflection::is_inner_ylt_refl_v<T>; #endif #if __cpp_concepts >= 201907L template <typename Type> concept user_defined_config_by_ADL = std::is_same_v<decltype(set_sp_config(std::declval<Type*>())),struct_pack::sp_config>; #else template <typename T, typename = void> struct user_defined_config_by_ADL_impl : std::false_type {}; template <typename T> struct user_defined_config_by_ADL_impl<T, std::void_t< std::enable_if_t<std::is_same_v<decltype(set_sp_config(std::declval<T*>())),struct_pack::sp_config>>>> : std::true_type {}; template <typename T> constexpr bool user_defined_config_by_ADL = user_defined_config_by_ADL_impl<T>::value; #endif template<typename T> constexpr decltype(auto) delay_sp_config_eval() { if constexpr (sizeof(T)==0) { return (T*)nullptr; } else { return (sp_config*)nullptr; } } #if __cpp_concepts >= 201907L template<typename T> concept has_default_config = std::is_same_v<decltype(set_default(decltype(delay_sp_config_eval<T>()){})),struct_pack::sp_config>; #else template <typename T, typename = void> struct has_default_config_impl : std::false_type {}; template <typename T> struct has_default_config_impl<T, std::void_t< std::enable_if_t<std::is_same_v<decltype(set_default(decltype(delay_sp_config_eval<T>()){})),struct_pack::sp_config>>>> : std::true_type {}; template <typename T> constexpr bool has_default_config = has_default_config_impl<T>::value; #endif #if __cpp_concepts >= 201907L template <typename Type> concept user_defined_config = requires { Type::struct_pack_config; }; #else template <typename T, typename = void> struct user_defined_config_impl : std::false_type {}; template <typename T> struct user_defined_config_impl<T, std::void_t<decltype(T::struct_pack_config)>> : std::true_type {}; template <typename T> constexpr bool user_defined_config = user_defined_config_impl<T>::value; #endif struct memory_reader; #if __cpp_concepts >= 201907L template <typename Type> concept user_defined_serialization = requires (Type& t) { sp_serialize_to(std::declval<struct_pack::detail::memory_writer&>(),(const Type&)t); {sp_deserialize_to(std::declval<struct_pack::detail::memory_reader&>(),t)} -> std::same_as<struct_pack::err_code>; {sp_get_needed_size((const Type&)t)}->std::same_as<std::size_t>; }; template <typename Type> concept user_defined_type_name = requires { { sp_set_type_name((Type*)nullptr) } -> std::same_as<std::string_view>; }; #else template <typename T, typename = void> struct user_defined_serialization_impl : std::false_type {}; template <typename T> struct user_defined_serialization_impl<T, std::void_t< decltype(sp_serialize_to(std::declval<struct_pack::detail::memory_writer&>(),std::declval<const T&>())), std::enable_if<std::is_same_v<decltype(sp_deserialize_to(std::declval<struct_pack::detail::memory_reader&>(),std::declval<T&>())), struct_pack::err_code>, std::enable_if<std::is_same_v<decltype(sp_get_needed_size(std::declval<const T&>())), std::string_view>>>>> : std::true_type {}; template <typename Type> constexpr bool user_defined_serialization = user_defined_serialization_impl<Type>::value; template <typename T, typename = void> struct user_defined_type_name_impl : std::false_type {}; template <typename T> struct user_defined_type_name_impl<T, std::void_t< std::enable_if<std::is_same_v<decltype(sp_set_type_name((T*)nullptr)), std::string_view>>>> : std::true_type {}; template <typename Type> constexpr bool user_defined_type_name = user_defined_type_name_impl<Type>::value; #endif #if __cpp_concepts >= 201907L template <typename Type> concept tuple_size = requires(Type tuple) { std::tuple_size<remove_cvref_t<Type>>::value; }; #else template <typename T, typename = void> struct tuple_size_impl : std::false_type {}; template <typename T> struct tuple_size_impl<T, std::void_t< decltype(std::tuple_size<remove_cvref_t<T>>::value)>> : std::true_type {}; template <typename T> constexpr bool tuple_size = tuple_size_impl<T>::value; #endif #if __cpp_concepts >= 201907L template <typename Type> concept array = requires(Type arr) { arr.size(); std::tuple_size<remove_cvref_t<Type>>{}; }; #else template <typename T, typename = void> struct array_impl : std::false_type {}; template <typename T> struct array_impl<T, std::void_t< decltype(std::declval<T>().size()), decltype(std::tuple_size<remove_cvref_t<T>>{})>> : std::true_type {}; template <typename T> constexpr bool array = array_impl<T>::value; #endif template <class T> constexpr bool c_array = std::is_array_v<T> && std::extent_v<remove_cvref_t<T>> > 0; #if __cpp_concepts >= 201907L template <typename Type> concept pair = requires(Type p) { typename remove_cvref_t<Type>::first_type; typename remove_cvref_t<Type>::second_type; p.first; p.second; }; #else template <typename T, typename = void> struct pair_impl : std::false_type {}; template <typename T> struct pair_impl<T, std::void_t< typename remove_cvref_t<T>::first_type, typename remove_cvref_t<T>::second_type, decltype(std::declval<T>().first), decltype(std::declval<T>().second)>> : std::true_type {}; template <typename T> constexpr bool pair = pair_impl<T>::value; #endif #if __cpp_concepts >= 201907L template <typename Type> concept unique_ptr = requires(Type ptr) { ptr.operator*(); typename remove_cvref_t<Type>::element_type; } &&!requires(Type ptr, Type ptr2) { ptr = ptr2; }; #else template <typename T, typename = void> struct unique_ptr_impl : std::false_type {}; template <typename T> struct unique_ptr_impl<T, std::void_t< typename remove_cvref_t<T>::element_type, decltype(std::declval<T>().operator*())>> : std::true_type {}; template <typename T> constexpr bool unique_ptr = unique_ptr_impl<T>::value; #endif template <typename Type> constexpr inline bool is_compatible_v = false; template <typename Type, uint64_t version> constexpr inline bool is_compatible_v<compatible<Type,version>> = true; template <typename Type> constexpr inline bool is_variant_v = false; template <typename... args> constexpr inline bool is_variant_v<std::variant<args...>> = true; template <typename T> constexpr inline bool is_trivial_tuple = false; template <typename T> class varint; template <typename T> class sint; template <typename T> constexpr bool varintable_t = std::is_same_v<T, varint<int32_t>> || std::is_same_v<T, varint<int64_t>> || std::is_same_v<T, varint<uint32_t>> || std::is_same_v<T, varint<uint64_t>>; template <typename T> constexpr bool sintable_t = std::is_same_v<T, sint<int32_t>> || std::is_same_v<T, sint<int64_t>>; template <typename T, uint64_t parent_tag = 0 > constexpr bool varint_t = varintable_t<T> || sintable_t<T> || ((parent_tag&struct_pack::ENCODING_WITH_VARINT) && (std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t> || std::is_same_v<T,int64_t> || std::is_same_v<T,uint64_t>)); template <typename Type> constexpr inline bool is_trivial_view_v = false; template <typename... Args> constexpr uint64_t get_parent_tag(); template <typename T, bool ignore_compatible_field = false, uint64_t parent_tag = 0> struct is_trivial_serializable { private: template<typename U,uint64_t parent_tag_=0, std::size_t... I> static constexpr bool class_visit_helper(std::index_sequence<I...>) { return (is_trivial_serializable<std::tuple_element_t<I, U>, ignore_compatible_field,parent_tag_>::value && ...); } static constexpr bool solve() { if constexpr (user_defined_serialization<T>) { return false; } else if constexpr (std::is_same_v<T,std::monostate>) { return true; } else if constexpr (std::is_abstract_v<T>) { return false; } else if constexpr (varint_t<T,parent_tag>) { return false; } else if constexpr (is_compatible_v<T> || is_trivial_view_v<T>) { return ignore_compatible_field; } else if constexpr (std::is_enum_v<T> || std::is_fundamental_v<T> || bitset<T> #if (__GNUC__ || __clang__) && defined(STRUCT_PACK_ENABLE_INT128) || std::is_same_v<__int128,T> || std::is_same_v<unsigned __int128,T> #endif ) { return true; } else if constexpr (array<T>) { return is_trivial_serializable<typename T::value_type, ignore_compatible_field>::value; } else if constexpr (c_array<T>) { return is_trivial_serializable<typename std::remove_all_extents<T>::type, ignore_compatible_field>::value; } else if constexpr (!pair<T> && tuple<T> && !is_trivial_tuple<T>) { return false; } else if constexpr (user_defined_refl<T>) { return false; } else if constexpr (container<T> || ylt::reflection::optional<T> || is_variant_v<T> || unique_ptr<T> || ylt::reflection::expected<T> || container_adapter<T>) { return false; } else if constexpr (pair<T>) { return is_trivial_serializable<typename T::first_type, ignore_compatible_field>::value && is_trivial_serializable<typename T::second_type, ignore_compatible_field>::value; } else if constexpr (is_trivial_tuple<T>) { return class_visit_helper<T>(std::make_index_sequence<std::tuple_size_v<T>>{}); } else if constexpr (std::is_class_v<T>) { constexpr auto tag = get_parent_tag<T>(); using U = decltype(get_types<T>()); return class_visit_helper<U , tag>(std::make_index_sequence<std::tuple_size_v<U>>{}); } else return false; } public: static inline constexpr bool value = is_trivial_serializable::solve(); }; } template <typename T, typename = std::enable_if_t<detail::is_trivial_serializable<T>::value>> struct trivial_view; namespace detail { #if __cpp_concepts < 201907L template <typename T, typename = void> struct trivially_copyable_container_impl : std::false_type {}; template <typename T> struct trivially_copyable_container_impl<T, std::void_t<std::enable_if_t<is_trivial_serializable<typename T::value_type>::value>>> : std::true_type {}; template <typename Type> constexpr bool trivially_copyable_container = continuous_container<Type> && trivially_copyable_container_impl<Type>::value; #else template <typename Type> constexpr bool trivially_copyable_container = continuous_container<Type> && requires(Type container) { requires is_trivial_serializable<typename Type::value_type>::value; }; #endif template <typename Type> constexpr inline bool is_trivial_view_v<struct_pack::trivial_view<Type>> = true; template <typename T> constexpr std::size_t members_count() { return ylt::reflection::members_count_v<T>; } template<typename Object,typename Visitor> constexpr decltype(auto) STRUCT_PACK_INLINE visit_members(Object &&object, Visitor &&visitor) { using type = remove_cvref_t<decltype(object)>; constexpr auto Count = struct_pack::members_count<type>; if constexpr (Count == 0 && std::is_class_v<type> && !std::is_same_v<type, std::monostate>) { static_assert(!sizeof(type), "1. If the struct is empty, which is not allowed in struct_pack type system.\n" "2. If the strut is not empty, it means struct_pack can't calculate your struct members' count. You can use macro YLT_REFL(Typename, field1, field2...)."); } // If you see any structured binding error in the follow line, it // means struct_pack can't calculate your struct's members count // correctly. // The best way is use macro YLT_REFL(Typename, field1, field2...) // See the src/struct_pack/example/non_aggregated_type.cpp for more details. // // You can also to mark it manually. // For example, there is a struct named Hello, // and it has 3 members. // // You can mark it as: // // template <> // constexpr size_t struct_pack::members_count<Hello> = 3; return ylt::reflection::visit_members<Object, Visitor, Count>(std::forward<Object>(object), std::forward<Visitor>(visitor)); } } // namespace detail #if __cpp_concepts >= 201907L template <typename T> concept checkable_reader_t = reader_t<T> && requires(T t) { t.check(std::size_t{}); }; #else template <typename T, typename = void> struct checkable_reader_t_impl : std::false_type {}; template <typename T> struct checkable_reader_t_impl< T, std::void_t<decltype(std::declval<T>().check(std::size_t{}))>> : std::true_type {}; template <typename T> constexpr bool checkable_reader_t = reader_t<T> &&checkable_reader_t_impl<T>::value; #endif } // namespace struct_pack // This marco is only for compatible with old version. Please instead it by YLT_REFL #define STRUCT_PACK_REFL(Type,...) \ YLT_REFL(Type,__VA_ARGS__)