// Copyright (c) 2016-2024 Antony Polukhin // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_PFR_DETAIL_CORE14_CLASSIC_HPP #define BOOST_PFR_DETAIL_CORE14_CLASSIC_HPP #pragma once #include #include #include // metaprogramming stuff #include #include #include #include #include #include #include #include #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wmissing-braces" # pragma clang diagnostic ignored "-Wundefined-inline" # pragma clang diagnostic ignored "-Wundefined-internal" # pragma clang diagnostic ignored "-Wmissing-field-initializers" #endif namespace boost { namespace pfr { namespace detail { ///////////////////// General utility stuff template struct identity { typedef T type; }; template constexpr T construct_helper() noexcept { // adding const here allows to deal with copyable only types return {}; } template constexpr size_array fields_count_and_type_ids_with_zeros() noexcept; template constexpr auto flat_array_of_type_ids() noexcept; ///////////////////// All the stuff for representing Type as integer and converting integer back to type namespace typeid_conversions { ///////////////////// Helper constants and typedefs #ifdef _MSC_VER # pragma warning( push ) // '<<': check operator precedence for possible error; use parentheses to clarify precedence # pragma warning( disable : 4554 ) #endif constexpr std::size_t native_types_mask = 31; constexpr std::size_t bits_per_extension = 3; constexpr std::size_t extension_mask = ( static_cast((1 << bits_per_extension) - 1) << static_cast(sizeof(std::size_t) * 8 - bits_per_extension) ); constexpr std::size_t native_ptr_type = ( static_cast(1) << static_cast(sizeof(std::size_t) * 8 - bits_per_extension) ); constexpr std::size_t native_const_ptr_type = ( static_cast(2) << static_cast(sizeof(std::size_t) * 8 - bits_per_extension) ); constexpr std::size_t native_const_volatile_ptr_type = ( static_cast(3) << static_cast(sizeof(std::size_t) * 8 - bits_per_extension) ); constexpr std::size_t native_volatile_ptr_type = ( static_cast(4) << static_cast(sizeof(std::size_t) * 8 - bits_per_extension) ); constexpr std::size_t native_ref_type = ( static_cast(5) << static_cast(sizeof(std::size_t) * 8 - bits_per_extension) ); template using if_extension = std::enable_if_t< (Index & extension_mask) == Extension >*; ///////////////////// Helper functions template constexpr std::size_t type_to_id_extension_apply(std::size_t ext) noexcept { constexpr std::size_t native_id = (Unptr & native_types_mask); constexpr std::size_t extensions = (Unptr & ~native_types_mask); static_assert( !((extensions >> bits_per_extension) & native_types_mask), "====================> Boost.PFR: Too many extensions for a single field (something close to `int************************** p;` is in the POD type)." ); return (extensions >> bits_per_extension) | native_id | ext; } template using remove_1_ext = size_t_< ((Index & ~native_types_mask) << bits_per_extension) | (Index & native_types_mask) >; #ifdef _MSC_VER # pragma warning( pop ) #endif ///////////////////// Forward declarations template constexpr std::size_t type_to_id(identity) noexcept; template constexpr std::size_t type_to_id(identity) noexcept; template constexpr std::size_t type_to_id(identity) noexcept; template constexpr std::size_t type_to_id(identity) noexcept; template constexpr std::size_t type_to_id(identity) noexcept; template constexpr std::size_t type_to_id(identity, std::enable_if_t::value>* = nullptr) noexcept; template constexpr std::size_t type_to_id(identity, std::enable_if_t::value>* = nullptr) noexcept; template constexpr std::size_t type_to_id(identity, std::enable_if_t::value>* = nullptr) noexcept; template constexpr size_array type_to_id(identity, std::enable_if_t::value && !std::is_empty::value && !std::is_union::value>* = 0) noexcept; template constexpr auto id_to_type(size_t_, if_extension = nullptr) noexcept; template constexpr auto id_to_type(size_t_, if_extension = nullptr) noexcept; template constexpr auto id_to_type(size_t_, if_extension = nullptr) noexcept; template constexpr auto id_to_type(size_t_, if_extension = nullptr) noexcept; template constexpr auto id_to_type(size_t_, if_extension = nullptr) noexcept; ///////////////////// Definitions of type_to_id and id_to_type for fundamental types /// @cond #define BOOST_MAGIC_GET_REGISTER_TYPE(Type, Index) \ constexpr std::size_t type_to_id(identity) noexcept { \ return Index; \ } \ constexpr Type id_to_type( size_t_ ) noexcept { \ return detail::construct_helper(); \ } \ /**/ /// @endcond // Register all base types here BOOST_MAGIC_GET_REGISTER_TYPE(unsigned char , 1) BOOST_MAGIC_GET_REGISTER_TYPE(unsigned short , 2) BOOST_MAGIC_GET_REGISTER_TYPE(unsigned int , 3) BOOST_MAGIC_GET_REGISTER_TYPE(unsigned long , 4) BOOST_MAGIC_GET_REGISTER_TYPE(unsigned long long , 5) BOOST_MAGIC_GET_REGISTER_TYPE(signed char , 6) BOOST_MAGIC_GET_REGISTER_TYPE(short , 7) BOOST_MAGIC_GET_REGISTER_TYPE(int , 8) BOOST_MAGIC_GET_REGISTER_TYPE(long , 9) BOOST_MAGIC_GET_REGISTER_TYPE(long long , 10) BOOST_MAGIC_GET_REGISTER_TYPE(char , 11) BOOST_MAGIC_GET_REGISTER_TYPE(wchar_t , 12) BOOST_MAGIC_GET_REGISTER_TYPE(char16_t , 13) BOOST_MAGIC_GET_REGISTER_TYPE(char32_t , 14) BOOST_MAGIC_GET_REGISTER_TYPE(float , 15) BOOST_MAGIC_GET_REGISTER_TYPE(double , 16) BOOST_MAGIC_GET_REGISTER_TYPE(long double , 17) BOOST_MAGIC_GET_REGISTER_TYPE(bool , 18) BOOST_MAGIC_GET_REGISTER_TYPE(void* , 19) BOOST_MAGIC_GET_REGISTER_TYPE(const void* , 20) BOOST_MAGIC_GET_REGISTER_TYPE(volatile void* , 21) BOOST_MAGIC_GET_REGISTER_TYPE(const volatile void* , 22) BOOST_MAGIC_GET_REGISTER_TYPE(std::nullptr_t , 23) constexpr std::size_t tuple_begin_tag = 24; constexpr std::size_t tuple_end_tag = 25; #undef BOOST_MAGIC_GET_REGISTER_TYPE ///////////////////// Definitions of type_to_id and id_to_type for types with extensions and nested types template constexpr std::size_t type_to_id(identity) noexcept { constexpr auto unptr = typeid_conversions::type_to_id(identity{}); static_assert( std::is_same::value, "====================> Boost.PFR: Pointers to user defined types are not supported." ); return typeid_conversions::type_to_id_extension_apply(native_ptr_type); } template constexpr std::size_t type_to_id(identity) noexcept { constexpr auto unptr = typeid_conversions::type_to_id(identity{}); static_assert( std::is_same::value, "====================> Boost.PFR: Const pointers to user defined types are not supported." ); return typeid_conversions::type_to_id_extension_apply(native_const_ptr_type); } template constexpr std::size_t type_to_id(identity) noexcept { constexpr auto unptr = typeid_conversions::type_to_id(identity{}); static_assert( std::is_same::value, "====================> Boost.PFR: Const volatile pointers to user defined types are not supported." ); return typeid_conversions::type_to_id_extension_apply(native_const_volatile_ptr_type); } template constexpr std::size_t type_to_id(identity) noexcept { constexpr auto unptr = typeid_conversions::type_to_id(identity{}); static_assert( std::is_same::value, "====================> Boost.PFR: Volatile pointers to user defined types are not supported." ); return typeid_conversions::type_to_id_extension_apply(native_volatile_ptr_type); } template constexpr std::size_t type_to_id(identity) noexcept { constexpr auto unptr = typeid_conversions::type_to_id(identity{}); static_assert( std::is_same::value, "====================> Boost.PFR: References to user defined types are not supported." ); return typeid_conversions::type_to_id_extension_apply(native_ref_type); } template constexpr std::size_t type_to_id(identity, std::enable_if_t::value>*) noexcept { return typeid_conversions::type_to_id(identity::type >{}); } template constexpr std::size_t type_to_id(identity, std::enable_if_t::value>*) noexcept { static_assert(!std::is_empty::value, "====================> Boost.PFR: Empty classes/structures as members are not supported."); return 0; } template constexpr std::size_t type_to_id(identity, std::enable_if_t::value>*) noexcept { static_assert( !std::is_union::value, "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); return 0; } template constexpr size_array type_to_id(identity, std::enable_if_t::value && !std::is_empty::value && !std::is_union::value>*) noexcept { constexpr auto t = detail::flat_array_of_type_ids(); size_array result {{tuple_begin_tag}}; constexpr bool requires_tuplening = ( (t.count_nonzeros() != 1) || (t.count_nonzeros() == t.count_from_opening_till_matching_parenthis_seq(0, tuple_begin_tag, tuple_end_tag)) ); if (requires_tuplening) { for (std::size_t i = 0; i < t.size(); ++i) result.data[i + 1] = t.data[i]; result.data[result.size() - 1] = tuple_end_tag; } else { for (std::size_t i = 0; i < t.size(); ++i) result.data[i] = t.data[i]; } return result; } template constexpr auto id_to_type(size_t_, if_extension) noexcept { typedef decltype( typeid_conversions::id_to_type(remove_1_ext()) )* res_t; return detail::construct_helper(); } template constexpr auto id_to_type(size_t_, if_extension) noexcept { typedef const decltype( typeid_conversions::id_to_type(remove_1_ext()) )* res_t; return detail::construct_helper(); } template constexpr auto id_to_type(size_t_, if_extension) noexcept { typedef const volatile decltype( typeid_conversions::id_to_type(remove_1_ext()) )* res_t; return detail::construct_helper(); } template constexpr auto id_to_type(size_t_, if_extension) noexcept { typedef volatile decltype( typeid_conversions::id_to_type(remove_1_ext()) )* res_t; return detail::construct_helper(); } template constexpr auto id_to_type(size_t_, if_extension) noexcept { static_assert(!Index, "====================> Boost.PFR: References are not supported"); return nullptr; } } // namespace typeid_conversions ///////////////////// Structure that remembers types as integers on a `constexpr operator Type()` call struct ubiq_val { std::size_t* ref_; template constexpr void assign(const T& typeids) const noexcept { for (std::size_t i = 0; i < T::size(); ++i) ref_[i] = typeids.data[i]; } constexpr void assign(std::size_t val) const noexcept { ref_[0] = val; } template constexpr operator Type() const noexcept { constexpr auto typeids = typeid_conversions::type_to_id(identity{}); assign(typeids); return detail::construct_helper(); } }; ///////////////////// Structure that remembers size of the type on a `constexpr operator Type()` call struct ubiq_sizes { std::size_t& ref_; template constexpr operator Type() const noexcept { ref_ = sizeof(Type); return detail::construct_helper(); } }; ///////////////////// Returns array of (offsets without accounting alignments). Required for keeping places for nested type ids template constexpr size_array get_type_offsets() noexcept { typedef size_array array_t; array_t sizes{}; T tmp{ ubiq_sizes{sizes.data[I]}... }; (void)tmp; array_t offsets{{0}}; for (std::size_t i = 1; i < N; ++i) offsets.data[i] = offsets.data[i - 1] + sizes.data[i - 1]; return offsets; } ///////////////////// Returns array of typeids and zeros if constructor of a type accepts sizeof...(I) parameters template constexpr void* flat_type_to_array_of_type_ids(std::size_t* types, std::index_sequence) noexcept { static_assert( N <= sizeof(T), "====================> Boost.PFR: Bit fields are not supported." ); constexpr auto offsets = detail::get_type_offsets(); T tmp{ ubiq_val{types + get(offsets) * 3}... }; (void)types; (void)tmp; (void)offsets; // If type is empty offsets are not used return nullptr; } ///////////////////// Returns array of typeids and zeros template constexpr size_array fields_count_and_type_ids_with_zeros() noexcept { size_array types{}; constexpr std::size_t N = detail::fields_count(); detail::flat_type_to_array_of_type_ids(types.data, detail::make_index_sequence()); return types; } ///////////////////// Returns array of typeids without zeros template constexpr auto flat_array_of_type_ids() noexcept { constexpr auto types = detail::fields_count_and_type_ids_with_zeros(); constexpr std::size_t count = types.count_nonzeros(); size_array res{}; std::size_t j = 0; for (std::size_t i = 0; i < decltype(types)::size(); ++i) { if (types.data[i]) { res.data[j] = types.data[i]; ++ j; } } return res; } ///////////////////// Convert array of typeids into sequence_tuple::tuple template constexpr auto as_flat_tuple_impl(std::index_sequence) noexcept; template constexpr sequence_tuple::tuple<> as_flat_tuple_impl(std::index_sequence<>) noexcept { return sequence_tuple::tuple<>{}; } template constexpr auto increment_index_sequence(std::index_sequence) noexcept { return std::index_sequence{}; } template constexpr auto prepare_subtuples(size_t_, size_t_, size_t_) noexcept { static_assert(SubtupleLength == 0, "====================> Boost.PFR: Internal error while representing nested field as tuple"); return typeid_conversions::id_to_type(size_t_{}); } template constexpr auto prepare_subtuples(size_t_, size_t_, size_t_) noexcept { static_assert(sizeof(T) == 0, "====================> Boost.PFR: Internal error while representing nested field as tuple"); return int{}; } template constexpr auto prepare_subtuples(size_t_, size_t_, size_t_) noexcept { static_assert(SubtupleLength > 2, "====================> Boost.PFR: Internal error while representing nested field as tuple"); constexpr auto seq = detail::make_index_sequence{}; return detail::as_flat_tuple_impl( detail::increment_index_sequence(seq) ); } template constexpr Array remove_subtuples(Array indexes_plus_1, const Array& subtuple_lengths) noexcept { for (std::size_t i = 0; i < subtuple_lengths.size(); ++i) { if (subtuple_lengths.data[i]) { const std::size_t skips_count = subtuple_lengths.data[i]; for (std::size_t j = i + 1; j < skips_count + i; ++j) { indexes_plus_1.data[j] = 0; } i += skips_count - 1; } } return indexes_plus_1; } template constexpr size_array resize_dropping_zeros_and_decrementing(size_t_, const Array& a) noexcept { size_array result{}; std::size_t result_indx = 0; for (std::size_t i = 0; i < a.size(); ++i) { if (a.data[i]) { result.data[result_indx] = static_cast(a.data[i] - 1); ++ result_indx; } } return result; } template constexpr auto as_flat_tuple_impl_drop_helpers(std::index_sequence, std::index_sequence) noexcept { constexpr auto a = detail::flat_array_of_type_ids(); constexpr size_array subtuples_length {{ a.count_from_opening_till_matching_parenthis_seq(First, typeid_conversions::tuple_begin_tag, typeid_conversions::tuple_end_tag), a.count_from_opening_till_matching_parenthis_seq(I, typeid_conversions::tuple_begin_tag, typeid_conversions::tuple_end_tag)... }}; constexpr size_array type_indexes_with_subtuple_internals {{ 1, 1 + I - First...}}; constexpr auto type_indexes_plus_1_and_zeros_as_skips = detail::remove_subtuples(type_indexes_with_subtuple_internals, subtuples_length); constexpr auto new_size = size_t_{}; constexpr auto type_indexes = detail::resize_dropping_zeros_and_decrementing(new_size, type_indexes_plus_1_and_zeros_as_skips); typedef sequence_tuple::tuple< decltype(detail::prepare_subtuples( size_t_< a.data[ First + type_indexes.data[INew] ] >{}, // id of type size_t_< First + type_indexes.data[INew] >{}, // index of current id in `a` size_t_< subtuples_length.data[ type_indexes.data[INew] ] >{} // if id of type is tuple, then length of that tuple ))... > subtuples_uncleanuped_t; return subtuples_uncleanuped_t{}; } template constexpr std::size_t count_skips_in_array(std::size_t begin_index, std::size_t end_index, const Array& a) noexcept { std::size_t skips = 0; for (std::size_t i = begin_index; i < end_index; ++i) { if (a.data[i] == typeid_conversions::tuple_begin_tag) { const std::size_t this_tuple_size = a.count_from_opening_till_matching_parenthis_seq(i, typeid_conversions::tuple_begin_tag, typeid_conversions::tuple_end_tag) - 1; skips += this_tuple_size; i += this_tuple_size - 1; } } return skips; } template constexpr auto as_flat_tuple_impl(std::index_sequence) noexcept { constexpr auto a = detail::flat_array_of_type_ids(); constexpr std::size_t count_of_I = sizeof...(I); return detail::as_flat_tuple_impl_drop_helpers( std::index_sequence{}, detail::make_index_sequence< 1 + count_of_I - count_skips_in_array(First, First + count_of_I, a) >{} ); } template constexpr auto internal_tuple_with_same_alignment() noexcept { typedef typename std::remove_cv::type type; static_assert( std::is_trivial::value && std::is_standard_layout::value, "====================> Boost.PFR: Type can not be reflected without Loophole or C++17, because it's not POD" ); static_assert(!std::is_reference::value, "====================> Boost.PFR: Not applyable"); constexpr auto res = detail::as_flat_tuple_impl( detail::make_index_sequence< decltype(detail::flat_array_of_type_ids())::size() >() ); return res; } template using internal_tuple_with_same_alignment_t = decltype( detail::internal_tuple_with_same_alignment() ); ///////////////////// Flattening struct ubiq_is_flat_refelectable { bool& is_flat_refelectable; template constexpr operator Type() const noexcept { is_flat_refelectable = std::is_fundamental>::value; return {}; } }; template constexpr bool is_flat_refelectable(std::index_sequence) noexcept { constexpr std::size_t fields = sizeof...(I); bool result[fields] = {static_cast(I)...}; const T v{ ubiq_is_flat_refelectable{result[I]}... }; (void)v; for (std::size_t i = 0; i < fields; ++i) { if (!result[i]) { return false; } } return true; } template constexpr bool is_flat_refelectable(std::index_sequence<>) noexcept { return true; ///< all empty structs always flat refelectable } template auto tie_as_flat_tuple(T& lvalue) noexcept { static_assert( !std::is_union::value, "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); using type = std::remove_cv_t; using tuple_type = internal_tuple_with_same_alignment_t; offset_based_getter getter; return boost::pfr::detail::make_flat_tuple_of_references(lvalue, getter, size_t_<0>{}, size_t_{}); } template auto tie_as_tuple(T& val) noexcept { static_assert( !std::is_union::value, "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); static_assert( boost::pfr::detail::is_flat_refelectable( detail::make_index_sequence()>{} ), "====================> Boost.PFR: Not possible in C++14 to represent that type without loosing information. Change type definition or enable C++17" ); return boost::pfr::detail::tie_as_flat_tuple(val); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////// Structure that can be converted to copy of anything struct ubiq_constructor_constexpr_copy { std::size_t ignore; template constexpr operator Type() const noexcept { static_assert( std::is_trivially_destructible::value, "====================> Boost.PFR: One of the fields in the type passed to `for_each_field` has non trivial destructor." ); return {}; } }; ///////////////////// template struct is_constexpr_aggregate_initializable { template static constexpr void* constexpr_aggregate_initializer() noexcept { T2 tmp{ ubiq_constructor_constexpr_copy{I2}... }; (void)tmp; return nullptr; } template () > static std::true_type test(long) noexcept; static std::false_type test(...) noexcept; static constexpr bool value = decltype(test(0)){}; }; template void for_each_field_in_depth(T& t, F&& f, std::index_sequence, identity...); template void for_each_field_in_depth(T& t, F&& f, std::index_sequence<>, identity...); template struct next_step { T& t; F& f; template operator Field() const { boost::pfr::detail::for_each_field_in_depth( t, std::forward(f), IndexSeq{}, identity{}..., identity{} ); return {}; } }; template void for_each_field_in_depth(T& t, F&& f, std::index_sequence, identity...) { (void)std::add_const_t>{ Fields{}..., next_step, Fields...>{t, f}, ubiq_constructor_constexpr_copy{I}... }; } template void for_each_field_in_depth(T& lvalue, F&& f, std::index_sequence<>, identity...) { using tuple_type = sequence_tuple::tuple; offset_based_getter>, tuple_type> getter; std::forward(f)( boost::pfr::detail::make_flat_tuple_of_references(lvalue, getter, size_t_<0>{}, size_t_{}) ); } template void for_each_field_dispatcher_1(T& t, F&& f, std::index_sequence, std::true_type /*is_flat_refelectable*/) { std::forward(f)( boost::pfr::detail::tie_as_flat_tuple(t) ); } template void for_each_field_dispatcher_1(T& t, F&& f, std::index_sequence, std::false_type /*is_flat_refelectable*/) { boost::pfr::detail::for_each_field_in_depth( t, std::forward(f), std::index_sequence{} ); } template void for_each_field_dispatcher(T& t, F&& f, std::index_sequence) { static_assert( !std::is_union::value, "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." ); static_assert(is_constexpr_aggregate_initializable::value, "====================> Boost.PFR: T must be a constexpr initializable type"); constexpr bool is_flat_refelectable_val = detail::is_flat_refelectable( std::index_sequence{} ); detail::for_each_field_dispatcher_1( t, std::forward(f), std::index_sequence{}, std::integral_constant{} ); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #ifdef __clang__ # pragma clang diagnostic pop #endif }}} // namespace boost::pfr::detail #endif // BOOST_PFR_DETAIL_CORE14_CLASSIC_HPP