2
0
mirror of https://github.com/boostorg/pfr.git synced 2026-01-19 16:32:13 +00:00

Multiple fixes for the structured bindings pack implementation (#221)

This commit is contained in:
Antony Polukhin
2025-09-11 21:51:07 +03:00
committed by GitHub
parent d9fde1f2a0
commit fc2dba87d6
10 changed files with 70 additions and 69 deletions

View File

@@ -53,7 +53,7 @@ BOOST_PFR_BEGIN_MODULE_EXPORT
template <std::size_t I, class T>
constexpr decltype(auto) get(const T& val) noexcept {
#if BOOST_PFR_USE_CPP26
const auto &[... members] = val;
const auto& [... members] = val;
return std::forward_like<const T &>(members...[I]);
#else
return detail::sequence_tuple::get<I>(detail::tie_as_tuple(val));
@@ -63,23 +63,23 @@ constexpr decltype(auto) get(const T& val) noexcept {
/// \overload get
template <std::size_t I, class T>
constexpr decltype(auto) get(T& val
#if !BOOST_PFR_USE_CPP17
#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26
, std::enable_if_t<std::is_assignable<T, T>::value>* = nullptr
#endif
) noexcept {
#if BOOST_PFR_USE_CPP26
auto &[... members] = val;
auto& [... members] = val;
return std::forward_like<T &>(members...[I]);
#else
return detail::sequence_tuple::get<I>( detail::tie_as_tuple(val) );
#endif
}
#if !BOOST_PFR_USE_CPP17
#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26
/// \overload get
template <std::size_t I, class T>
constexpr auto get(T&, std::enable_if_t<!std::is_assignable<T, T>::value>* = nullptr) noexcept {
static_assert(sizeof(T) && false, "====================> Boost.PFR: Calling boost::pfr::get on non const non assignable type is allowed only in C++17");
static_assert(sizeof(T) && false, "====================> Boost.PFR: Calling boost::pfr::get on non const non assignable type is allowed only in C++17 and later");
return 0;
}
#endif
@@ -89,7 +89,7 @@ constexpr auto get(T&, std::enable_if_t<!std::is_assignable<T, T>::value>* = nul
template <std::size_t I, class T>
constexpr auto get(T&& val, std::enable_if_t< std::is_rvalue_reference<T&&>::value>* = nullptr) noexcept {
#if BOOST_PFR_USE_CPP26
auto &&[... members] = std::forward<T>(val);
auto&& [... members] = std::forward<T>(val);
return std::move(members...[I]);
#else
return std::move(detail::sequence_tuple::get<I>( detail::tie_as_tuple(val) ));
@@ -107,18 +107,18 @@ constexpr const U& get(const T& val) noexcept {
/// \overload get
template <class U, class T>
constexpr U& get(T& val
#if !BOOST_PFR_USE_CPP17
#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26
, std::enable_if_t<std::is_assignable<T, T>::value>* = nullptr
#endif
) noexcept {
return detail::sequence_tuple::get_by_type_impl<U&>( detail::tie_as_tuple(val) );
}
#if !BOOST_PFR_USE_CPP17
#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26
/// \overload get
template <class U, class T>
constexpr U& get(T&, std::enable_if_t<!std::is_assignable<T, T>::value>* = nullptr) noexcept {
static_assert(sizeof(T) && false, "====================> Boost.PFR: Calling boost::pfr::get on non const non assignable type is allowed only in C++17");
static_assert(sizeof(T) && false, "====================> Boost.PFR: Calling boost::pfr::get on non const non assignable type is allowed only in C++17 and later");
return 0;
}
#endif
@@ -163,7 +163,7 @@ using tuple_element_t = typename tuple_element<I, T>::type;
template <class T>
constexpr auto structure_to_tuple(const T& val) {
#if BOOST_PFR_USE_CPP26
const auto &[... members] = val;
const auto& [... members] = val;
return std::make_tuple(members...);
#else
return detail::make_stdtuple_from_tietuple(
@@ -193,7 +193,7 @@ constexpr auto structure_to_tuple(const T& val) {
template <class T>
constexpr auto structure_tie(const T& val) noexcept {
#if BOOST_PFR_USE_CPP26
const auto &[... members] = val;
const auto& [... members] = val;
return std::tie(std::forward_like<const T &>(members)...);
#else
return detail::make_conststdtiedtuple_from_tietuple(
@@ -207,12 +207,12 @@ constexpr auto structure_tie(const T& val) noexcept {
/// \overload structure_tie
template <class T>
constexpr auto structure_tie(T& val
#if !BOOST_PFR_USE_CPP17
#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26
, std::enable_if_t<std::is_assignable<T, T>::value>* = nullptr
#endif
) noexcept {
#if BOOST_PFR_USE_CPP26
auto &[... members] = val;
auto& [... members] = val;
return std::tie(std::forward_like<T &>(members)...);
#else
return detail::make_stdtiedtuple_from_tietuple(detail::tie_as_tuple(val),
@@ -220,11 +220,11 @@ constexpr auto structure_tie(T& val
#endif
}
#if !BOOST_PFR_USE_CPP17
#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26
/// \overload structure_tie
template <class T>
constexpr auto structure_tie(T&, std::enable_if_t<!std::is_assignable<T, T>::value>* = nullptr) noexcept {
static_assert(sizeof(T) && false, "====================> Boost.PFR: Calling boost::pfr::structure_tie on non const non assignable type is allowed only in C++17");
static_assert(sizeof(T) && false, "====================> Boost.PFR: Calling boost::pfr::structure_tie on non const non assignable type is allowed only in C++17 and later modes");
return 0;
}
#endif

View File

@@ -15,8 +15,7 @@
namespace boost::pfr::detail {
template<class T>
constexpr auto tie_as_tuple(T &val) noexcept
{
constexpr auto tie_as_tuple(T &val) noexcept {
static_assert(!std::is_union<T>::value,
"====================> Boost.PFR: For safety reasons it is forbidden to reflect "
"unions. See `Reflection of unions` section in the docs for more info.");
@@ -24,6 +23,17 @@ constexpr auto tie_as_tuple(T &val) noexcept
return sequence_tuple::tuple<std::add_lvalue_reference_t<decltype(members)>...>{members...};
}
template <class T, class F, std::size_t... I>
constexpr void for_each_field_dispatcher(T& t, F&& f, std::index_sequence<I...>) {
static_assert(
!std::is_union<T>::value,
"====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info."
);
std::forward<F>(f)(
detail::tie_as_tuple(t)
);
}
} // namespace boost::pfr::detail
#endif

View File

@@ -332,20 +332,6 @@ constexpr std::size_t fields_count_lower_bound_unbounded(int, size_t_<0>) noexce
#endif
///////////////////// Choosing between array size, unbounded binary search, and linear search followed by unbounded binary search.
#if BOOST_PFR_USE_CPP26
template<class T>
constexpr auto fields_count_dispatch_impl(const T &t) noexcept
{
const auto &[... elts] = t;
return std::integral_constant<std::size_t, sizeof...(elts)>{};
}
template<class T>
constexpr auto fields_count_dispatch() noexcept
{
return decltype(fields_count_dispatch_impl(std::declval<const T &>()))::value;
}
#else
template <class T>
constexpr auto fields_count_dispatch(long, long, std::false_type /*are_preconditions_met*/) noexcept {
return 0;
@@ -358,6 +344,27 @@ constexpr auto fields_count_dispatch(long, long, std::true_type /*are_preconditi
return sizeof(T) / sizeof(std::remove_all_extents_t<T>);
}
#if BOOST_PFR_USE_CPP26
template<class T>
constexpr auto fields_count_dispatch_impl(const T &t) noexcept
{
const auto &[... elts] = t;
return std::integral_constant<std::size_t, sizeof...(elts)>{};
}
template<class T>
constexpr auto fields_count_dispatch(long, int, std::true_type /*are_preconditions_met*/) noexcept
-> std::enable_if_t<std::is_scalar<T>::value, std::size_t>
{
return 1;
}
template<class T>
constexpr auto fields_count_dispatch(int, int, std::true_type /*are_preconditions_met*/) noexcept
{
return decltype(detail::fields_count_dispatch_impl(std::declval<const T &>()))::value;
}
#else
template <class T>
constexpr auto fields_count_dispatch(long, int, std::true_type /*are_preconditions_met*/) noexcept
-> decltype(sizeof(T{}))
@@ -442,12 +449,8 @@ constexpr std::size_t fields_count() noexcept {
constexpr bool no_errors =
type_is_complete && type_is_not_a_reference && type_fields_are_move_constructible
&& type_is_not_polymorphic && type_is_aggregate;
#if BOOST_PFR_USE_CPP26
constexpr std::size_t result = detail::fields_count_dispatch<type>();
#else
constexpr std::size_t result
= detail::fields_count_dispatch<type>(1L, 1L, std::integral_constant<bool, no_errors>{});
#endif
detail::assert_first_not_base<type, result>(1L);
#ifndef __cpp_lib_is_aggregate

View File

@@ -24,7 +24,8 @@ namespace boost { namespace pfr { namespace detail {
template <class T, class F>
constexpr void for_each_field(T&& value, F&& func) {
#if BOOST_PFR_USE_CPP26
if constexpr (std::is_aggregate_v<T> || std::is_bounded_array_v<std::remove_reference_t<T>>) {
using no_ref = std::remove_reference_t<T>;
if constexpr (std::is_aggregate_v<no_ref> || std::is_bounded_array_v<no_ref>) {
auto &&[... members] = value;
::boost::pfr::detail::for_each_field_impl(value,
std::forward<F>(func),

View File

@@ -21,23 +21,20 @@ namespace boost { namespace pfr { namespace detail {
template <std::size_t Index>
using size_t_ = std::integral_constant<std::size_t, Index >;
#if BOOST_PFR_USE_CPP26
template<class T, class F, class I, class = decltype(std::declval<F>()(std::declval<T>(), I{}))>
constexpr void for_each_field_impl_apply(T &&v, F &&f, I i, long)
{
template <class T, class F, class I, class = decltype(std::declval<F>()(std::declval<T>(), I{}))>
constexpr void for_each_field_impl_apply(T&& v, F&& f, I i, long) {
std::forward<F>(f)(std::forward<T>(v), i);
}
template<class T, class F, class I>
constexpr void for_each_field_impl_apply(T &&v, F &&f, I /*i*/, int)
{
template <class T, class F, class I>
constexpr void for_each_field_impl_apply(T&& v, F&& f, I /*i*/, int) {
std::forward<F>(f)(std::forward<T>(v));
}
#if BOOST_PFR_USE_CPP26
template<class T, class F, std::size_t... I>
constexpr void for_each_field_impl(T &t, F &&f, std::index_sequence<I...>, auto move_values)
{
if constexpr (std::is_aggregate_v<T> || std::is_bounded_array_v<std::remove_reference_t<T>>) {
constexpr void for_each_field_impl(T& t, F&& f, std::index_sequence<I...>, auto move_values) {
if constexpr (std::is_aggregate_v<T> || std::is_bounded_array_v<T>) {
auto &&[... members] = t;
if constexpr (move_values)
(detail::for_each_field_impl_apply(std::move(members...[I]),
@@ -57,18 +54,7 @@ constexpr void for_each_field_impl(T &t, F &&f, std::index_sequence<I...>, auto
(detail::for_each_field_impl_apply(t, std::forward<F>(f), size_t_<I>{}, 1L), ...);
}
}
#else
template <class T, class F, class I, class = decltype(std::declval<F>()(std::declval<T>(), I{}))>
constexpr void for_each_field_impl_apply(T&& v, F&& f, I i, long) {
std::forward<F>(f)(std::forward<T>(v), i);
}
template <class T, class F, class I>
constexpr void for_each_field_impl_apply(T&& v, F&& f, I /*i*/, int) {
std::forward<F>(f)(std::forward<T>(v));
}
#if !defined(__cpp_fold_expressions) || __cpp_fold_expressions < 201603
#elif !defined(__cpp_fold_expressions) || __cpp_fold_expressions < 201603
template <class T, class F, std::size_t... I>
constexpr void for_each_field_impl(T& t, F&& f, std::index_sequence<I...>, std::false_type /*move_values*/) {
const int v[] = {0, (
@@ -98,7 +84,6 @@ constexpr void for_each_field_impl(T& t, F&& f, std::index_sequence<I...>, std::
(detail::for_each_field_impl_apply(sequence_tuple::get<I>(std::move(t)), std::forward<F>(f), size_t_<I>{}, 1L), ...);
}
#endif
#endif
}}} // namespace boost::pfr::detail
#endif // BOOST_PFR_DETAIL_FOR_EACH_FIELD_IMPL_HPP

View File

@@ -54,7 +54,7 @@ local REQUIRE_LOOPHOLE =
;
local REQUIRE_VSB =
[ check-target-builds ../core//compiler_supports_vsb : : <build>no ]
;
;
local VARIADIC_STRUCTURED_BINDING_ENGINE = <define>BOOST_PFR_USE_LOOPHOLE=0 <define>BOOST_PFR_USE_CPP17=0 <define>BOOST_PFR_USE_CPP26=1 $(REQUIRE_VSB) ;
local STRUCTURED_BINDING_ENGINE = <define>BOOST_PFR_USE_LOOPHOLE=0 <define>BOOST_PFR_USE_CPP17=1 <define>BOOST_PFR_USE_CPP26=0 [ requires cxx17_structured_bindings ] ;

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2024 Antony Polukhin
// Copyright (c) 2024-2025 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)
@@ -9,11 +9,7 @@
#include <cstdint>
#if defined(__clang__)
# if SIZE_MAX > (1ULL << 32) - 1
# define ARRAY_MAX (SIZE_MAX >> 3)
# else
# define ARRAY_MAX SIZE_MAX
# endif
# define ARRAY_MAX INT_MAX
# define OBJECT_MAX SIZE_MAX
#elif defined(__GNUC__)
# define ARRAY_MAX INT_MAX

View File

@@ -23,7 +23,7 @@ int main() {
// FIXME: https://github.com/boostorg/pfr/issues/131
#if defined(__clang__) && __cplusplus >= 202002L
# if BOOST_PFR_USE_LOOPHOLE == 0 && BOOST_PFR_USE_CPP17 == 0
# if BOOST_PFR_USE_LOOPHOLE == 0 && BOOST_PFR_USE_CPP17 == 0 && BOOST_PFR_USE_CPP26 == 0
# error This test should fail on classic engine
#endif

View File

@@ -18,8 +18,8 @@ struct aggr {
zero_array a;
};
static_assert(sizeof(zero_array) == 0);
static_assert(sizeof(aggr) == 0);
static_assert(sizeof(zero_array) == 0, "");
static_assert(sizeof(aggr) == 0, "");
int main() {
aggr a;

View File

@@ -6,6 +6,11 @@
// Detection of variadic structured binding support
#include <type_traits>
#include <version>
#if !(__cpp_structured_bindings >= 202411L && __cpp_lib_forward_like >= 202207L)
#error Compiler does not support the required features
#endif
struct MyPair {
int first;
@@ -21,3 +26,4 @@ auto test() {
int main() {
return ::test<MyPair>();
}