// Copyright (c) 2016-2021 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_FIELDS_COUNT_HPP #define BOOST_PFR_DETAIL_FIELDS_COUNT_HPP #pragma once #include #include #include #include #include // CHAR_BIT #include #include // metaprogramming stuff #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 { ///////////////////// Structure that can be converted to reference to anything struct ubiq_lref_constructor { std::size_t ignore; template constexpr operator Type&() const && noexcept { // tweak for template_unconstrained.cpp like cases return detail::unsafe_declval(); }; template constexpr operator Type&() const & noexcept { // tweak for optional_chrono.cpp like cases return detail::unsafe_declval(); }; }; ///////////////////// Structure that can be converted to rvalue reference to anything struct ubiq_rref_constructor { std::size_t ignore; template /*constexpr*/ operator Type&&() const && noexcept { // Allows initialization of rvalue reference fields and move-only types return detail::unsafe_declval(); }; }; #ifndef __cpp_lib_is_aggregate ///////////////////// Hand-made is_aggregate_initializable_n trait // Structure that can be converted to reference to anything except reference to T template struct ubiq_constructor_except { std::size_t ignore; template constexpr operator std::enable_if_t::value, Type&> () const noexcept; // Undefined }; template struct ubiq_constructor_except { std::size_t ignore; template constexpr operator std::enable_if_t::value, Type&&> () const noexcept; // Undefined }; // `std::is_constructible>` consumes a lot of time, so we made a separate lazy trait for it. template struct is_single_field_and_aggregate_initializable: std::false_type {}; template struct is_single_field_and_aggregate_initializable<1, T>: std::integral_constant< bool, !std::is_constructible::value>>::value > {}; // Hand-made is_aggregate trait: // Before C++20 aggregates could be constructed from `decltype(ubiq_?ref_constructor{I})...` but type traits report that // there's no constructor from `decltype(ubiq_?ref_constructor{I})...` // Special case for N == 1: `std::is_constructible` returns true if N == 1 and T is copy/move constructible. template struct is_aggregate_initializable_n { template static constexpr bool is_not_constructible_n(std::index_sequence) noexcept { return (!std::is_constructible::value && !std::is_constructible::value) || is_single_field_and_aggregate_initializable::value ; } static constexpr bool value = std::is_empty::value || std::is_array::value || std::is_fundamental::value || is_not_constructible_n(detail::make_index_sequence{}) ; }; #endif // #ifndef __cpp_lib_is_aggregate ///////////////////// Helper for SFINAE on fields count template ::value>::type> constexpr auto enable_if_constructible_helper(std::index_sequence) noexcept -> typename std::add_pointer::type; template ::value>::type> constexpr auto enable_if_constructible_helper(std::index_sequence) noexcept -> typename std::add_pointer::type; template (detail::make_index_sequence()) ) > using enable_if_constructible_helper_t = std::size_t; ///////////////////// Helpers for range size detection template using is_one_element_range = std::integral_constant; using multi_element_range = std::false_type; using one_element_range = std::true_type; ///////////////////// Non greedy fields count search. Templates instantiation depth is log(sizeof(T)), templates instantiation count is log(sizeof(T)). template constexpr std::size_t detect_fields_count(detail::one_element_range, long) noexcept { static_assert( Begin == Middle, "====================> Boost.PFR: Internal logic error." ); return Begin; } template constexpr std::size_t detect_fields_count(detail::multi_element_range, int) noexcept; template constexpr auto detect_fields_count(detail::multi_element_range, long) noexcept -> detail::enable_if_constructible_helper_t { constexpr std::size_t next_v = Middle + (Middle - Begin + 1) / 2; return detail::detect_fields_count(detail::is_one_element_range{}, 1L); } template constexpr std::size_t detect_fields_count(detail::multi_element_range, int) noexcept { constexpr std::size_t next_v = Begin + (Middle - Begin) / 2; return detail::detect_fields_count(detail::is_one_element_range{}, 1L); } ///////////////////// Greedy search. Templates instantiation depth is log(sizeof(T)), templates instantiation count is log(sizeof(T))*T in worst case. template constexpr auto detect_fields_count_greedy_remember(long) noexcept -> detail::enable_if_constructible_helper_t { return N; } template constexpr std::size_t detect_fields_count_greedy_remember(int) noexcept { return 0; } template constexpr std::size_t detect_fields_count_greedy(detail::one_element_range) noexcept { static_assert( Begin == Last, "====================> Boost.PFR: Internal logic error." ); return detail::detect_fields_count_greedy_remember(1L); } template constexpr std::size_t detect_fields_count_greedy(detail::multi_element_range) noexcept { constexpr std::size_t middle = Begin + (Last - Begin) / 2; constexpr std::size_t fields_count_big_range = detail::detect_fields_count_greedy( detail::is_one_element_range{} ); constexpr std::size_t small_range_begin = (fields_count_big_range ? 0 : Begin); constexpr std::size_t small_range_last = (fields_count_big_range ? 0 : middle); constexpr std::size_t fields_count_small_range = detail::detect_fields_count_greedy( detail::is_one_element_range{} ); return fields_count_big_range ? fields_count_big_range : fields_count_small_range; } ///////////////////// Choosing between array size, greedy and non greedy search. template constexpr auto detect_fields_count_dispatch(size_t_, long, long) noexcept -> typename std::enable_if::value, std::size_t>::type { return sizeof(T) / sizeof(typename std::remove_all_extents::type); } template constexpr auto detect_fields_count_dispatch(size_t_, long, int) noexcept -> decltype(sizeof(T{})) { constexpr std::size_t middle = N / 2 + 1; return detail::detect_fields_count(detail::multi_element_range{}, 1L); } template constexpr std::size_t detect_fields_count_dispatch(size_t_, int, int) noexcept { // T is not default aggregate initialzable. It means that at least one of the members is not default constructible, // so we have to check all the aggregate initializations for T up to N parameters and return the bigest succeeded // (we can not use binary search for detecting fields count). return detail::detect_fields_count_greedy(detail::multi_element_range{}); } ///////////////////// Returns fields count template constexpr std::size_t fields_count() noexcept { using type = std::remove_cv_t; static_assert( !std::is_reference::value, "====================> Boost.PFR: Attempt to get fields count on a reference. This is not allowed because that could hide an issue and different library users expect different behavior in that case." ); static_assert( std::is_copy_constructible>::value || ( std::is_move_constructible>::value && std::is_move_assignable>::value ), "====================> Boost.PFR: Type and each field in the type must be copy constructible (or move constructible and move assignable)." ); static_assert( !std::is_polymorphic::value, "====================> Boost.PFR: Type must have no virtual function, because otherwise it is not aggregate initializable." ); #ifdef __cpp_lib_is_aggregate static_assert( std::is_aggregate::value // Does not return `true` for built-in types. || std::is_scalar::value, "====================> Boost.PFR: Type must be aggregate initializable." ); #endif // Can't use the following. See the non_std_layout.cpp test. //#if !BOOST_PFR_USE_CPP17 // static_assert( // std::is_standard_layout::value, // Does not return `true` for structs that have non standard layout members. // "Type must be aggregate initializable." // ); //#endif constexpr std::size_t max_fields_count = (sizeof(type) * CHAR_BIT); // We multiply by CHAR_BIT because the type may have bitfields in T constexpr std::size_t result = detail::detect_fields_count_dispatch(size_t_{}, 1L, 1L); #ifndef __cpp_lib_is_aggregate static_assert( is_aggregate_initializable_n::value, "====================> Boost.PFR: Types with user specified constructors (non-aggregate initializable types) are not supported." ); #endif static_assert( result != 0 || std::is_empty::value || std::is_fundamental::value || std::is_reference::value, "====================> Boost.PFR: If there's no other failed static asserts then something went wrong. Please report this issue to the github along with the structure you're reflecting." ); return result; } }}} // namespace boost::pfr::detail #ifdef __clang__ # pragma clang diagnostic pop #endif #endif // BOOST_PFR_DETAIL_FIELDS_COUNT_HPP