Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions include/boost/pfr/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions include/boost/pfr/detail/fields_count.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,36 @@ 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 <class T, std::size_t... I, class /*Enable*/ = std::enable_if_t<std::is_copy_constructible<T>::value && (sizeof...(I) <= 1)>>
constexpr auto enable_if_initializable_helper(std::index_sequence<I...>) noexcept
-> std::add_pointer_t<decltype(T{ubiq_lref_constructor{I}...})>;

template <class T, std::size_t... I, class /*Enable*/ = std::enable_if_t<!std::is_copy_constructible<T>::value && (sizeof...(I) <= 1)>>
constexpr auto enable_if_initializable_helper(std::index_sequence<I...>) noexcept
-> std::add_pointer_t<decltype(T{ubiq_rref_constructor{I}...})>;

template <class T, std::size_t... I, class /*Enable*/ = std::enable_if_t<std::is_copy_constructible<T>::value && (sizeof...(I) >= 2)>>
constexpr auto enable_if_initializable_helper(std::index_sequence<I...>) noexcept
-> std::add_pointer_t<decltype(T(ubiq_lref_constructor{I}...))>;

template <class T, std::size_t... I, class /*Enable*/ = std::enable_if_t<!std::is_copy_constructible<T>::value && (sizeof...(I) >= 2)>>
constexpr auto enable_if_initializable_helper(std::index_sequence<I...>) noexcept
-> std::add_pointer_t<decltype(T(ubiq_rref_constructor{I}...))>;
#else
template <class T, std::size_t... I, class /*Enable*/ = std::enable_if_t<std::is_copy_constructible<T>::value>>
constexpr auto enable_if_initializable_helper(std::index_sequence<I...>) noexcept
-> std::add_pointer_t<decltype(T{ubiq_lref_constructor{I}...})>;

template <class T, std::size_t... I, class /*Enable*/ = std::enable_if_t<!std::is_copy_constructible<T>::value>>
constexpr auto enable_if_initializable_helper(std::index_sequence<I...>) noexcept
-> std::add_pointer_t<decltype(T{ubiq_rref_constructor{I}...})>;
#endif

template <class T, std::size_t N, class U = std::size_t, class /*Enable*/ = decltype(detail::enable_if_initializable_helper<T>(detail::make_index_sequence<N>()))>
using enable_if_initializable_helper_t = U;
Expand Down
23 changes: 23 additions & 0 deletions test/core/run/struct_with_array_field.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <boost/pfr/core.hpp>

// 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<single_int_array> == 1, "single_int_array");
static_assert(boost::pfr::tuple_size_v<single_char_array> == 1, "single_char_array");
static_assert(boost::pfr::tuple_size_v<single_big_array> == 1, "single_big_array");
#endif
}