diff --git a/include/boost/pfr/config.hpp b/include/boost/pfr/config.hpp index 4912545f..433e5930 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -88,6 +88,14 @@ # endif #endif +#ifndef BOOST_PFR_USE_CPP20 +# if __cpp_concepts >= 201907L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +# define BOOST_PFR_USE_CPP20 1 +# else +# define BOOST_PFR_USE_CPP20 0 +# endif +#endif + #if (!BOOST_PFR_USE_CPP26 && !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_LOOPHOLE) # if (defined(_MSC_VER) && _MSC_VER < 1916) ///< in Visual Studio 2017 v15.9 PFR library with classic engine normally works # define BOOST_PFR_NOT_SUPPORTED 1 diff --git a/include/boost/pfr/detail/fields_count.hpp b/include/boost/pfr/detail/fields_count.hpp index 4a8c5a2b..900d9734 100644 --- a/include/boost/pfr/detail/fields_count.hpp +++ b/include/boost/pfr/detail/fields_count.hpp @@ -182,6 +182,28 @@ constexpr auto assert_first_not_base(long) noexcept ///////////////////// Helpers for initializable detection // Note that these take O(N) compile time and memory! +// In C++20+, the N=1 probe uses braces (same as C++17: correct for empty aggregates +// and non-default-constructible trailing members; T{ubiq} is aggregate-init, not +// copy-construction). N>=2 probes use parenthesized aggregate init T(ubiq...) +// (P0960R3), which does not perform brace elision, so structs with C-style array +// members are counted correctly (1 field, not sizeof(array)/sizeof(element)). +#if BOOST_PFR_USE_CPP20 && !BOOST_PFR_USE_CPP26 && !BOOST_PFR_USE_CPP26_REFLECTION +template ::value && (sizeof...(I) <= 1)>> +constexpr auto enable_if_initializable_helper(std::index_sequence) noexcept + -> std::add_pointer_t; + +template ::value && (sizeof...(I) <= 1)>> +constexpr auto enable_if_initializable_helper(std::index_sequence) noexcept + -> std::add_pointer_t; + +template ::value && (sizeof...(I) >= 2)>> +constexpr auto enable_if_initializable_helper(std::index_sequence) noexcept + -> std::add_pointer_t; + +template ::value && (sizeof...(I) >= 2)>> +constexpr auto enable_if_initializable_helper(std::index_sequence) noexcept + -> std::add_pointer_t; +#else template ::value>> constexpr auto enable_if_initializable_helper(std::index_sequence) noexcept -> std::add_pointer_t; @@ -189,6 +211,7 @@ constexpr auto enable_if_initializable_helper(std::index_sequence) noexcep template ::value>> constexpr auto enable_if_initializable_helper(std::index_sequence) noexcept -> std::add_pointer_t; +#endif template (detail::make_index_sequence()))> using enable_if_initializable_helper_t = U; diff --git a/test/core/run/struct_with_array_field.cpp b/test/core/run/struct_with_array_field.cpp new file mode 100644 index 00000000..8215b9de --- /dev/null +++ b/test/core/run/struct_with_array_field.cpp @@ -0,0 +1,23 @@ +#include + +// Structs whose only field is a C-style array. Without the C++20 fix, +// brace elision in T{ubiq...} lets individual array elements absorb separate +// ubiq values, so fields_count returns the array element count instead of 1. +// +// Note: structs that mix an array field with other fields (e.g. +// struct { int arr[4]; int x; }) are not tested here because the ubiq-based +// field-count probe cannot directly initialize an array member in +// parenthesized aggregate init (arrays are not copy-constructible), so the +// count would be wrong in both C++17 and C++20 modes for such types. + +struct single_int_array { int data[4]; }; +struct single_char_array { char buf[16]; }; +struct single_big_array { int data[32]; }; + +int main() { +#if BOOST_PFR_USE_CPP20 + static_assert(boost::pfr::tuple_size_v == 1, "single_int_array"); + static_assert(boost::pfr::tuple_size_v == 1, "single_char_array"); + static_assert(boost::pfr::tuple_size_v == 1, "single_big_array"); +#endif +}