From 114ef6106040a1427730e1abb986b48ae56e672d Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Fri, 30 Jan 2026 12:03:53 +0300 Subject: [PATCH 01/13] C++26 reflection based implementation --- include/boost/pfr/config.hpp | 8 ++ include/boost/pfr/detail/core_name.hpp | 4 +- .../pfr/detail/core_name26_reflection.hpp | 79 +++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 include/boost/pfr/detail/core_name26_reflection.hpp diff --git a/include/boost/pfr/config.hpp b/include/boost/pfr/config.hpp index bd92fe5a..182a2eeb 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -61,6 +61,14 @@ #endif #endif +#ifndef BOOST_PFR_USE_CPP26_REFLECTION +#ifdef __cpp_lib_reflection +#define BOOST_PFR_USE_CPP26_REFLECTION 1 +#else +#define BOOST_PFR_USE_CPP26_REFLECTION 0 +#endif +#endif + #ifndef BOOST_PFR_USE_CPP17 # ifdef __cpp_structured_bindings # define BOOST_PFR_USE_CPP17 1 diff --git a/include/boost/pfr/detail/core_name.hpp b/include/boost/pfr/detail/core_name.hpp index f266ef20..1585440e 100644 --- a/include/boost/pfr/detail/core_name.hpp +++ b/include/boost/pfr/detail/core_name.hpp @@ -19,7 +19,9 @@ // // The whole functional of extracting field's names is build on top of those // two functions. -#if BOOST_PFR_CORE_NAME_ENABLED +#if BOOST_PFR_USE_CPP26_REFLECTION +#include +#elif BOOST_PFR_CORE_NAME_ENABLED #include #else #include diff --git a/include/boost/pfr/detail/core_name26_reflection.hpp b/include/boost/pfr/detail/core_name26_reflection.hpp new file mode 100644 index 00000000..dcdaa995 --- /dev/null +++ b/include/boost/pfr/detail/core_name26_reflection.hpp @@ -0,0 +1,79 @@ +// Copyright (c) 2016-2026 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) + + +// Initial implementation by Bela Schaum, https://github.com/schaumb +// The way to make it union and UB free by X-Ryl669, https://github.com/X-Ryl669 +// + +#ifndef BOOST_PFR_DETAIL_CORE_NAME26_REFLECTION_HPP +#define BOOST_PFR_DETAIL_CORE_NAME26_REFLECTION_HPP +#pragma once + +#include + +#include +#include +#include +#include + +#if !defined(BOOST_PFR_INTERFACE_UNIT) +#include +#include +#include // for std::addressof +#include +#endif + +namespace boost { namespace pfr { namespace detail { + +template +constexpr std::string_view get_name() 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( + !std::is_array::value, + "====================> Boost.PFR: It is impossible to extract name from old C array since it doesn't have named members" + ); + + return identifier_of( + nonstatic_data_members_of( + ^^T, + std::meta::access_context::current() + ).at(I) + ); +} + +template +constexpr auto tie_as_names_tuple_impl(std::index_sequence) noexcept { + return detail::sequence_tuple::make_sequence_tuple(detail::get_name()...); +} + +template +constexpr auto tie_as_names_tuple() noexcept { + return detail::tie_as_names_tuple_impl(detail::make_index_sequence()>{}); +} + +template +constexpr void for_each_field_with_name(T&& value, F&& func) { + return boost::pfr::detail::for_each_field( + std::forward(value), + [&func](auto&& field, auto index) mutable { + using IndexType = decltype(index); + using FieldType = decltype(field); + constexpr auto name = boost::pfr::detail::get_name, IndexType::value>(); + if constexpr (std::is_invocable_v) { + std::forward(func)(name, std::forward(field), index); + } else { + std::forward(func)(name, std::forward(field)); + } + }); +} + +}}} // namespace boost::pfr::detail + +#endif // BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP + From 08a78e7471876512da39b4002f37ea6dc3695862 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Fri, 30 Jan 2026 12:50:46 +0300 Subject: [PATCH 02/13] fields count via C++26 reflection --- include/boost/pfr/detail/fields_count.hpp | 28 +++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/include/boost/pfr/detail/fields_count.hpp b/include/boost/pfr/detail/fields_count.hpp index 062381e3..80e60417 100644 --- a/include/boost/pfr/detail/fields_count.hpp +++ b/include/boost/pfr/detail/fields_count.hpp @@ -207,7 +207,7 @@ using is_one_element_range = std::integral_constant; using multi_element_range = std::false_type; using one_element_range = std::true_type; -#if !BOOST_PFR_USE_CPP26 +#if !BOOST_PFR_USE_CPP26 && !BOOST_PFR_USE_CPP26_REFLECTION ///////////////////// Fields count next expected compiler limitation constexpr std::size_t fields_count_compiler_limitation_next(std::size_t n) noexcept { #if defined(_MSC_VER) && (_MSC_VER <= 1920) @@ -344,7 +344,31 @@ constexpr auto fields_count_dispatch(long, long, std::true_type /*are_preconditi return sizeof(T) / sizeof(std::remove_all_extents_t); } -#if BOOST_PFR_USE_CPP26 +#if BOOST_PFR_USE_CPP26_REFLECTION +template +constexpr auto fields_count_dispatch_impl(const T &) noexcept +{ + return std::integral_constant{}; +} + +template +constexpr auto fields_count_dispatch(long, int, std::true_type /*are_preconditions_met*/) noexcept + -> std::enable_if_t::value, std::size_t> +{ + return 1; +} + +template +constexpr auto fields_count_dispatch(int, int, std::true_type /*are_preconditions_met*/) noexcept +{ + return decltype(detail::fields_count_dispatch_impl(std::declval()))::value; +} +#elif BOOST_PFR_USE_CPP26 template constexpr auto fields_count_dispatch_impl(const T &t) noexcept { From bad6129b5638a3ca45c0b1fb5fd27301e2df26fc Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Fri, 30 Jan 2026 13:16:14 +0300 Subject: [PATCH 03/13] Implement core in reflection --- doc/pfr.qbk | 13 ++-- include/boost/pfr/detail/core.hpp | 4 +- .../boost/pfr/detail/core26_reflection.hpp | 64 +++++++++++++++++++ 3 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 include/boost/pfr/detail/core26_reflection.hpp diff --git a/doc/pfr.qbk b/doc/pfr.qbk index 8e1e567c..f85c6da8 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -1,6 +1,6 @@ [library Boost.PFR [quickbook 1.6] - [version 2.3] + [version 2.4] [copyright 2016-2026 Antony Polukhin] [category Language Features Emulation] [license @@ -501,6 +501,7 @@ Boost.PFRs extraction of field name works with only `SimpleAggregate` types. By default Boost.PFR [*auto-detects your compiler abilities] and automatically defines the configuration macro into appropriate values. If you wish to override that behavior, just define: [table:linkmacro Macros [[Macro name] [Effect]] + [[*BOOST_PFR_USE_CPP26_REFLECTION*] [Define to `1` if you wish to override Boost.PFR choice and use C++26 Reflection. Define to `0` to override Boost.PFR choice and disable C++26 Reflection usage.]] [[*BOOST_PFR_USE_CPP26*] [Define to `1` if you wish to override Boost.PFR choice and use C++26 variadic structured bindings for reflection. Define to `0` to override Boost.PFR choice and disable C++26 variadic structured bindings usage.]] [[*BOOST_PFR_USE_CPP17*] [Define to `1` if you wish to override Boost.PFR choice and use C++17 structured bindings for reflection. Define to `0` to override Boost.PFR choice and disable C++17 structured bindings usage.]] [[*BOOST_PFR_USE_LOOPHOLE*] [Define to `1` if you wish to override Boost.PFR choice and exploit [@http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118 CWG 2118] for reflection. Define to `0` to override Boost.PFR choice and disable CWG 2118 usage.]] @@ -594,11 +595,13 @@ clang++ -std=c++20 -fmodule-file=boost_pfr.pcm boost_pfr.pcm usage_sample.cpp Short description: # at compile-time: use aggregate initialization to detect fields count in user-provided structure - * [*BOOST_PFR_USE_CPP26 == 1]: + * [*BOOST_PFR_USE_CPP26_REFLECTION == 1]: + # at compile-time: C++26 Reflection is used to make member pointers to fields + * [*BOOST_PFR_USE_CPP26_REFLECTION == 0 && BOOST_PFR_USE_CPP26 == 1]: # at compile-time: structured bindings are used to decompose a type `T` to known variadic amount of fields - * [*BOOST_PFR_USE_CPP26 == 0 && BOOST_PFR_USE_CPP17 == 1]: + * [*BOOST_PFR_USE_CPP26_REFLECTION == 0 && BOOST_PFR_USE_CPP26 == 0 && BOOST_PFR_USE_CPP17 == 1]: # at compile-time: structured bindings are used to decompose a type `T` to known amount of fields - * [*BOOST_PFR_USE_CPP26 == 0 && BOOST_PFR_USE_CPP17 == 0 && BOOST_PFR_USE_LOOPHOLE == 1]: + * [*BOOST_PFR_USE_CPP26_REFLECTION == 0 && BOOST_PFR_USE_CPP26 == 0 && BOOST_PFR_USE_CPP17 == 0 && BOOST_PFR_USE_LOOPHOLE == 1]: # at compile-time: use aggregate initialization to detect fields count in user-provided structure # at compile-time: make a structure that is convertible to anything and remember types it has been converted to during aggregate initialization of user-provided structure # at compile-time: using knowledge from previous steps create a tuple with exactly the same layout as in user-provided structure @@ -621,6 +624,8 @@ Description of the [*BOOST_PFR_USE_LOOPHOLE == 1] technique by its inventor Alex [h2 Field name retrieval] +If [*BOOST_PFR_USE_CPP26_REFLECTION == 1] then C++26 Reflection is used to get names. Otherwise: + # at compile-time: * Get references to members of an object of type `T` in `constexpr` function * Feed the reference from previous as a template parameter to a `constexpr` function with `template `. diff --git a/include/boost/pfr/detail/core.hpp b/include/boost/pfr/detail/core.hpp index 6c997964..25c17e79 100644 --- a/include/boost/pfr/detail/core.hpp +++ b/include/boost/pfr/detail/core.hpp @@ -13,7 +13,9 @@ // `boost::pfr::detail::for_each_field_dispatcher` functions. // // The whole PFR library is build on top of those two functions. -#if BOOST_PFR_USE_CPP26 +#if BOOST_PFR_USE_CPP26_REFLECTION +#include +#elif BOOST_PFR_USE_CPP26 #include #elif BOOST_PFR_USE_CPP17 # include diff --git a/include/boost/pfr/detail/core26_reflection.hpp b/include/boost/pfr/detail/core26_reflection.hpp new file mode 100644 index 00000000..9ba4b036 --- /dev/null +++ b/include/boost/pfr/detail/core26_reflection.hpp @@ -0,0 +1,64 @@ +// Copyright (c) 2025-2026 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_CORE26_REFLECTION_HPP +#define BOOST_PFR_DETAIL_CORE26_REFLECTION_HPP +#pragma once + +#include +#include +#include +#include + +#if !defined(BOOST_PFR_INTERFACE_UNIT) +#include +#include +#endif + +namespace boost::pfr::detail { + +template +consteval decltype(auto) reference_by_index(T &val) noexcept { + return val.*[: + reflect_constant(nonstatic_data_members_of( + ^^T, + std::meta::access_context::current() + ).at(I)) + :]; +} + +template +constexpr auto tie_as_tuple_impl(T &val, std::index_sequence) noexcept { + return sequence_tuple::tuple< + decltype(boost::pfr::detail::reference_by_index(val))... + >{ + boost::pfr::detail::reference_by_index(val)... + }; + +} + +template +constexpr auto tie_as_tuple(T &val) noexcept { + return detail::tie_as_tuple_impl( + val, + detail::make_index_sequence()>{} + ); + +} + +template +constexpr 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." + ); + std::forward(f)( + detail::tie_as_tuple(t) + ); +} + +} // namespace boost::pfr::detail + +#endif From c698cb4009e1dd6f1ca94d8ed53246140d69d305 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Fri, 30 Jan 2026 13:54:16 +0300 Subject: [PATCH 04/13] fixes and tweaks --- doc/pfr.qbk | 2 +- include/boost/pfr/core.hpp | 22 ++++++++++++------- .../boost/pfr/detail/core26_reflection.hpp | 10 +++++++-- include/boost/pfr/detail/fields_count.hpp | 5 +++++ modules/boost_pfr.cppm | 5 +++++ 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/doc/pfr.qbk b/doc/pfr.qbk index f85c6da8..37d9df1e 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -596,7 +596,7 @@ Short description: # at compile-time: use aggregate initialization to detect fields count in user-provided structure * [*BOOST_PFR_USE_CPP26_REFLECTION == 1]: - # at compile-time: C++26 Reflection is used to make member pointers to fields + # at compile-time: C++26 Reflection is used to make reference to field * [*BOOST_PFR_USE_CPP26_REFLECTION == 0 && BOOST_PFR_USE_CPP26 == 1]: # at compile-time: structured bindings are used to decompose a type `T` to known variadic amount of fields * [*BOOST_PFR_USE_CPP26_REFLECTION == 0 && BOOST_PFR_USE_CPP26 == 0 && BOOST_PFR_USE_CPP17 == 1]: diff --git a/include/boost/pfr/core.hpp b/include/boost/pfr/core.hpp index d621f273..5060172d 100644 --- a/include/boost/pfr/core.hpp +++ b/include/boost/pfr/core.hpp @@ -52,7 +52,9 @@ BOOST_PFR_BEGIN_MODULE_EXPORT /// \endcode template constexpr decltype(auto) get(const T& val) noexcept { -#if BOOST_PFR_USE_CPP26 +#if BOOST_PFR_USE_CPP26_REFLECTION + return detail::reference_by_index(val); +#elif BOOST_PFR_USE_CPP26 const auto& [... members] = val; return std::forward_like(members...[I]); #else @@ -67,7 +69,9 @@ constexpr decltype(auto) get(T& val , std::enable_if_t::value>* = nullptr #endif ) noexcept { -#if BOOST_PFR_USE_CPP26 +#if BOOST_PFR_USE_CPP26_REFLECTION + return detail::reference_by_index(val); +#elif BOOST_PFR_USE_CPP26 auto& [... members] = val; return std::forward_like(members...[I]); #else @@ -75,7 +79,7 @@ constexpr decltype(auto) get(T& val #endif } -#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26 +#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26 && !BOOST_PFR_USE_CPP26_REFLECTION /// \overload get template constexpr auto get(T&, std::enable_if_t::value>* = nullptr) noexcept { @@ -88,7 +92,9 @@ constexpr auto get(T&, std::enable_if_t::value>* = nul /// \overload get template constexpr auto get(T&& val, std::enable_if_t< std::is_rvalue_reference::value>* = nullptr) noexcept { -#if BOOST_PFR_USE_CPP26 +#if BOOST_PFR_USE_CPP26_REFLECTION + return std::move(detail::reference_by_index(val)); +#elif BOOST_PFR_USE_CPP26 auto&& [... members] = std::forward(val); return std::move(members...[I]); #else @@ -107,14 +113,14 @@ constexpr const U& get(const T& val) noexcept { /// \overload get template constexpr U& get(T& val -#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26 +#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26 && !BOOST_PFR_USE_CPP26_REFLECTION , std::enable_if_t::value>* = nullptr #endif ) noexcept { return detail::sequence_tuple::get_by_type_impl( detail::tie_as_tuple(val) ); } -#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26 +#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26 && !BOOST_PFR_USE_CPP26_REFLECTION /// \overload get template constexpr U& get(T&, std::enable_if_t::value>* = nullptr) noexcept { @@ -207,7 +213,7 @@ constexpr auto structure_tie(const T& val) noexcept { /// \overload structure_tie template constexpr auto structure_tie(T& val -#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26 +#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26 && !BOOST_PFR_USE_CPP26_REFLECTION , std::enable_if_t::value>* = nullptr #endif ) noexcept { @@ -220,7 +226,7 @@ constexpr auto structure_tie(T& val #endif } -#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26 +#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26 && !BOOST_PFR_USE_CPP26_REFLECTION /// \overload structure_tie template constexpr auto structure_tie(T&, std::enable_if_t::value>* = nullptr) noexcept { diff --git a/include/boost/pfr/detail/core26_reflection.hpp b/include/boost/pfr/detail/core26_reflection.hpp index 9ba4b036..617c9193 100644 --- a/include/boost/pfr/detail/core26_reflection.hpp +++ b/include/boost/pfr/detail/core26_reflection.hpp @@ -20,8 +20,8 @@ namespace boost::pfr::detail { template -consteval decltype(auto) reference_by_index(T &val) noexcept { - return val.*[: +consteval decltype(auto) member_ptr_by_index() noexcept { + return [: reflect_constant(nonstatic_data_members_of( ^^T, std::meta::access_context::current() @@ -29,6 +29,12 @@ consteval decltype(auto) reference_by_index(T &val) noexcept { :]; } +template +constexpr decltype(auto) reference_by_index(T &val) noexcept { + auto member_ptr = detail::member_ptr_by_index(); + return val.*member_ptr; +} + template constexpr auto tie_as_tuple_impl(T &val, std::index_sequence) noexcept { return sequence_tuple::tuple< diff --git a/include/boost/pfr/detail/fields_count.hpp b/include/boost/pfr/detail/fields_count.hpp index 80e60417..4a8c5a2b 100644 --- a/include/boost/pfr/detail/fields_count.hpp +++ b/include/boost/pfr/detail/fields_count.hpp @@ -16,6 +16,11 @@ #include #include #include // metaprogramming stuff + +#if BOOST_PFR_USE_CPP26_REFLECTION +#include +#endif + #endif #ifdef __clang__ diff --git a/modules/boost_pfr.cppm b/modules/boost_pfr.cppm index 62482b28..09bc882c 100644 --- a/modules/boost_pfr.cppm +++ b/modules/boost_pfr.cppm @@ -28,6 +28,11 @@ import std; #include #include #include + +#ifdef __cpp_lib_reflection +#include +#endif + #endif #define BOOST_PFR_INTERFACE_UNIT From 56c12f35d5107fc688446ca20340ed8df5171052 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Fri, 30 Jan 2026 14:21:31 +0300 Subject: [PATCH 05/13] fix --- include/boost/pfr/detail/core26_reflection.hpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/include/boost/pfr/detail/core26_reflection.hpp b/include/boost/pfr/detail/core26_reflection.hpp index 617c9193..d287d045 100644 --- a/include/boost/pfr/detail/core26_reflection.hpp +++ b/include/boost/pfr/detail/core26_reflection.hpp @@ -20,21 +20,15 @@ namespace boost::pfr::detail { template -consteval decltype(auto) member_ptr_by_index() noexcept { - return [: - reflect_constant(nonstatic_data_members_of( +consteval decltype(auto) reference_by_index(T &val) noexcept { + return val.[: + nonstatic_data_members_of( ^^T, std::meta::access_context::current() - ).at(I)) + ).at(I) :]; } -template -constexpr decltype(auto) reference_by_index(T &val) noexcept { - auto member_ptr = detail::member_ptr_by_index(); - return val.*member_ptr; -} - template constexpr auto tie_as_tuple_impl(T &val, std::index_sequence) noexcept { return sequence_tuple::tuple< From 25b5fd744d68ab7c133f5c207b4232c6df35a14a Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sat, 31 Jan 2026 12:59:34 +0300 Subject: [PATCH 06/13] tweaks --- include/boost/pfr/config.hpp | 3 ++- test/config/print_config.cpp | 1 + test/core/Jamfile.v2 | 16 +++++++++--- test/core/cpp26_reflection_detection.cpp | 33 ++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 test/core/cpp26_reflection_detection.cpp diff --git a/include/boost/pfr/config.hpp b/include/boost/pfr/config.hpp index 182a2eeb..3f02bbb2 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -63,7 +63,8 @@ #ifndef BOOST_PFR_USE_CPP26_REFLECTION #ifdef __cpp_lib_reflection -#define BOOST_PFR_USE_CPP26_REFLECTION 1 +// TODO: experimental. Not enabled by default for now +#define BOOST_PFR_USE_CPP26_REFLECTION 0 #else #define BOOST_PFR_USE_CPP26_REFLECTION 0 #endif diff --git a/test/config/print_config.cpp b/test/config/print_config.cpp index faff3ac9..6c0245ab 100644 --- a/test/config/print_config.cpp +++ b/test/config/print_config.cpp @@ -15,6 +15,7 @@ int main() { std::cout << "Platform info:" << '\n' << "BOOST_PFR_USE_CPP17 == " << BOOST_PFR_USE_CPP17 << '\n' << "BOOST_PFR_USE_CPP26 == " << BOOST_PFR_USE_CPP26 << '\n' + << "BOOST_PFR_USE_CPP26_REFLECTION == " << BOOST_PFR_USE_CPP26_REFLECTION << '\n' << "BOOST_PFR_USE_LOOPHOLE == " << BOOST_PFR_USE_LOOPHOLE << '\n' << "BOOST_PFR_USE_STD_MAKE_INTEGRAL_SEQUENCE == " << BOOST_PFR_USE_STD_MAKE_INTEGRAL_SEQUENCE << '\n' << "BOOST_PFR_HAS_GUARANTEED_COPY_ELISION == " << BOOST_PFR_HAS_GUARANTEED_COPY_ELISION << '\n' diff --git a/test/core/Jamfile.v2 b/test/core/Jamfile.v2 index 0428a0aa..6b9671b1 100644 --- a/test/core/Jamfile.v2 +++ b/test/core/Jamfile.v2 @@ -45,6 +45,9 @@ explicit compiler_supports_loophole ; mp-run-simple variadic_structured_binding_detection.cpp : : : : compiler_supports_vsb ; explicit compiler_supports_vsb ; +mp-run-simple cpp26_reflection_detection.cpp : : : : compiler_supports_cpp26_reflection ; +explicit compiler_supports_cpp26_reflection ; + ########## END of helpers to detect Loophole trick support @@ -55,11 +58,15 @@ local REQUIRE_LOOPHOLE = local REQUIRE_VSB = [ check-target-builds ../core//compiler_supports_vsb : : no ] ; +local REQUIRE_CPP26_REFLECTION = + [ check-target-builds ../core//compiler_supports_cpp26_reflection : : no ] + ; -local VARIADIC_STRUCTURED_BINDING_ENGINE = BOOST_PFR_USE_LOOPHOLE=0 BOOST_PFR_USE_CPP17=0 BOOST_PFR_USE_CPP26=1 $(REQUIRE_VSB) ; -local STRUCTURED_BINDING_ENGINE = BOOST_PFR_USE_LOOPHOLE=0 BOOST_PFR_USE_CPP17=1 BOOST_PFR_USE_CPP26=0 [ requires cxx17_structured_bindings ] ; -local LOOPHOLE_ENGINE = BOOST_PFR_USE_LOOPHOLE=1 BOOST_PFR_USE_CPP17=0 BOOST_PFR_USE_CPP26=0 $(REQUIRE_LOOPHOLE) ; -local CLASSIC_ENGINE = BOOST_PFR_USE_LOOPHOLE=0 BOOST_PFR_USE_CPP17=0 BOOST_PFR_USE_CPP26=0 $(DISABLE_ON_MSVC) ; +local CPP26_REFLECTION_ENGINE = BOOST_PFR_USE_LOOPHOLE=0 BOOST_PFR_USE_CPP17=0 BOOST_PFR_USE_CPP26=0 BOOST_PFR_USE_CPP26_REFLECTION=1 $(REQUIRE_CPP26_REFLECTION) ; +local VARIADIC_STRUCTURED_BINDING_ENGINE = BOOST_PFR_USE_LOOPHOLE=0 BOOST_PFR_USE_CPP17=0 BOOST_PFR_USE_CPP26=1 BOOST_PFR_USE_CPP26_REFLECTION=0 $(REQUIRE_VSB) ; +local STRUCTURED_BINDING_ENGINE = BOOST_PFR_USE_LOOPHOLE=0 BOOST_PFR_USE_CPP17=1 BOOST_PFR_USE_CPP26=0 BOOST_PFR_USE_CPP26_REFLECTION=0 [ requires cxx17_structured_bindings ] ; +local LOOPHOLE_ENGINE = BOOST_PFR_USE_LOOPHOLE=1 BOOST_PFR_USE_CPP17=0 BOOST_PFR_USE_CPP26=0 BOOST_PFR_USE_CPP26_REFLECTION=0 $(REQUIRE_LOOPHOLE) ; +local CLASSIC_ENGINE = BOOST_PFR_USE_LOOPHOLE=0 BOOST_PFR_USE_CPP17=0 BOOST_PFR_USE_CPP26=0 BOOST_PFR_USE_CPP26_REFLECTION=0 $(DISABLE_ON_MSVC) ; test-suite pfr_tests : @@ -116,6 +123,7 @@ local BLACKLIST_TESTS_FOR_CLASSIC = for local source_file in [ glob ./run/*.cpp ] [ glob ../../example/*.cpp ] { local target_name = $(source_file[1]:B) ; + pfr_tests += [ run $(source_file) : : : $(CPP26_REFLECTION_ENGINE) : $(target_name)_refl ] ; pfr_tests += [ run $(source_file) : : : $(VARIADIC_STRUCTURED_BINDING_ENGINE) : $(target_name)_vsb ] ; pfr_tests += [ run $(source_file) : : : $(STRUCTURED_BINDING_ENGINE) : $(target_name)_sb ] ; diff --git a/test/core/cpp26_reflection_detection.cpp b/test/core/cpp26_reflection_detection.cpp new file mode 100644 index 00000000..8c436cbb --- /dev/null +++ b/test/core/cpp26_reflection_detection.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2025-2026 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) + +// Detection of variadic structured binding support + +#include +#include + +#ifndef __cpp_lib_reflection +#error Compiler does not support the required features +#endif + +struct MyPair { + int first; + int second; +}; + +template +auto test() { + return T{}.[: + nonstatic_data_members_of( + ^^T, + std::meta::access_context::current() + ).at(0) + :]; +} + +int main() { + return ::test(); +} + From b7f8dfe109177a88184995549a0a9f23960b1472 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sat, 31 Jan 2026 13:03:44 +0300 Subject: [PATCH 07/13] fixes --- test/core/cpp26_reflection_detection.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/core/cpp26_reflection_detection.cpp b/test/core/cpp26_reflection_detection.cpp index 8c436cbb..f75e33f3 100644 --- a/test/core/cpp26_reflection_detection.cpp +++ b/test/core/cpp26_reflection_detection.cpp @@ -12,6 +12,8 @@ #error Compiler does not support the required features #endif +#include + struct MyPair { int first; int second; From b12551c5479ac70f21eca9628c5e3566c9949a5c Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sat, 31 Jan 2026 13:14:24 +0300 Subject: [PATCH 08/13] remove code duplication --- include/boost/pfr/core_name.hpp | 13 ++++++++++++- .../boost/pfr/detail/core_name14_disabled.hpp | 9 --------- include/boost/pfr/detail/core_name20_static.hpp | 16 ---------------- .../boost/pfr/detail/core_name26_reflection.hpp | 16 ---------------- test/core/cpp26_reflection_detection.cpp | 2 +- 5 files changed, 13 insertions(+), 43 deletions(-) diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index 186d29be..f50dba1a 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -106,7 +106,18 @@ names_as_array() noexcept { /// \endcode template constexpr void for_each_field_with_name(T&& value, F&& func) { - return boost::pfr::detail::for_each_field_with_name(std::forward(value), std::forward(func)); + return boost::pfr::detail::for_each_field( + std::forward(value), + [f = std::forward(func)](auto&& field, auto index) mutable { + using IndexType = decltype(index); + using FieldType = decltype(field); + constexpr auto name = boost::pfr::detail::get_name, IndexType::value>(); + if constexpr (std::is_invocable_v) { + f(name, std::forward(field), index); + } else { + f(name, std::forward(field)); + } + }); } BOOST_PFR_END_MODULE_EXPORT diff --git a/include/boost/pfr/detail/core_name14_disabled.hpp b/include/boost/pfr/detail/core_name14_disabled.hpp index 9d342781..115e8382 100644 --- a/include/boost/pfr/detail/core_name14_disabled.hpp +++ b/include/boost/pfr/detail/core_name14_disabled.hpp @@ -37,15 +37,6 @@ constexpr auto tie_as_names_tuple() noexcept { return detail::sequence_tuple::make_sequence_tuple(); } - -template -constexpr void for_each_field_with_name(T&& /* value */, F&& /* func */) { - static_assert( - sizeof(T) && false, - "====================> Boost.PFR: Field's names extracting functionality requires C++20." - ); -} - }}} // namespace boost::pfr::detail #endif // BOOST_PFR_DETAIL_CORE_NAME14_DISABLED_HPP diff --git a/include/boost/pfr/detail/core_name20_static.hpp b/include/boost/pfr/detail/core_name20_static.hpp index 4c615904..03fdfaae 100644 --- a/include/boost/pfr/detail/core_name20_static.hpp +++ b/include/boost/pfr/detail/core_name20_static.hpp @@ -241,22 +241,6 @@ constexpr auto tie_as_names_tuple() noexcept { return detail::tie_as_names_tuple_impl(detail::make_index_sequence()>{}); } -template -constexpr void for_each_field_with_name(T&& value, F&& func) { - return boost::pfr::detail::for_each_field( - std::forward(value), - [f = std::forward(func)](auto&& field, auto index) mutable { - using IndexType = decltype(index); - using FieldType = decltype(field); - constexpr auto name = boost::pfr::detail::get_name, IndexType::value>(); - if constexpr (std::is_invocable_v) { - f(name, std::forward(field), index); - } else { - f(name, std::forward(field)); - } - }); -} - }}} // namespace boost::pfr::detail #endif // BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP diff --git a/include/boost/pfr/detail/core_name26_reflection.hpp b/include/boost/pfr/detail/core_name26_reflection.hpp index dcdaa995..6af5221b 100644 --- a/include/boost/pfr/detail/core_name26_reflection.hpp +++ b/include/boost/pfr/detail/core_name26_reflection.hpp @@ -57,22 +57,6 @@ constexpr auto tie_as_names_tuple() noexcept { return detail::tie_as_names_tuple_impl(detail::make_index_sequence()>{}); } -template -constexpr void for_each_field_with_name(T&& value, F&& func) { - return boost::pfr::detail::for_each_field( - std::forward(value), - [&func](auto&& field, auto index) mutable { - using IndexType = decltype(index); - using FieldType = decltype(field); - constexpr auto name = boost::pfr::detail::get_name, IndexType::value>(); - if constexpr (std::is_invocable_v) { - std::forward(func)(name, std::forward(field), index); - } else { - std::forward(func)(name, std::forward(field)); - } - }); -} - }}} // namespace boost::pfr::detail #endif // BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP diff --git a/test/core/cpp26_reflection_detection.cpp b/test/core/cpp26_reflection_detection.cpp index f75e33f3..b809bc27 100644 --- a/test/core/cpp26_reflection_detection.cpp +++ b/test/core/cpp26_reflection_detection.cpp @@ -3,7 +3,7 @@ // 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) -// Detection of variadic structured binding support +// Detection of C++26 Reflection #include #include From 31f7680a401e19bd7a16e9ffe669fd675043d2ad Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sun, 1 Feb 2026 13:52:34 +0300 Subject: [PATCH 09/13] fix --- include/boost/pfr/core_name.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index f50dba1a..6b5d3ab1 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -108,14 +108,14 @@ template constexpr void for_each_field_with_name(T&& value, F&& func) { return boost::pfr::detail::for_each_field( std::forward(value), - [f = std::forward(func)](auto&& field, auto index) mutable { + [&func](auto&& field, auto index) mutable { using IndexType = decltype(index); using FieldType = decltype(field); constexpr auto name = boost::pfr::detail::get_name, IndexType::value>(); if constexpr (std::is_invocable_v) { - f(name, std::forward(field), index); + std::forward(func)(name, std::forward(field), index); } else { - f(name, std::forward(field)); + std::forward(func)(name, std::forward(field)); } }); } From 3692bdbfe9eed6f9be004daca83ad5bb485e4f5c Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sun, 1 Feb 2026 13:56:39 +0300 Subject: [PATCH 10/13] fix --- include/boost/pfr/core_name.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index 6b5d3ab1..1c086d43 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -26,6 +26,7 @@ #if !defined(BOOST_PFR_INTERFACE_UNIT) #include // for std::size_t +#include #endif /// \file boost/pfr/core_name.hpp @@ -108,7 +109,7 @@ template constexpr void for_each_field_with_name(T&& value, F&& func) { return boost::pfr::detail::for_each_field( std::forward(value), - [&func](auto&& field, auto index) mutable { + [&func](auto&& field, auto index) { using IndexType = decltype(index); using FieldType = decltype(field); constexpr auto name = boost::pfr::detail::get_name, IndexType::value>(); From f21598e6b5bf0befb39fade84880ff5c774707ca Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sun, 1 Feb 2026 20:51:18 +0300 Subject: [PATCH 11/13] fixes --- include/boost/pfr/core_name.hpp | 6 ++++++ include/boost/pfr/detail/core_name14_disabled.hpp | 14 +++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index 1c086d43..9196a94a 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -107,6 +107,7 @@ names_as_array() noexcept { /// \endcode template constexpr void for_each_field_with_name(T&& value, F&& func) { +#if BOOST_PFR_CORE_NAME_ENABLED return boost::pfr::detail::for_each_field( std::forward(value), [&func](auto&& field, auto index) { @@ -119,6 +120,11 @@ constexpr void for_each_field_with_name(T&& value, F&& func) { std::forward(func)(name, std::forward(field)); } }); +#else + boost::pfr::detail::report_name_reflection_mising_requirement(); + (void)value; + (void)func; +#endif } BOOST_PFR_END_MODULE_EXPORT diff --git a/include/boost/pfr/detail/core_name14_disabled.hpp b/include/boost/pfr/detail/core_name14_disabled.hpp index 115e8382..2e016056 100644 --- a/include/boost/pfr/detail/core_name14_disabled.hpp +++ b/include/boost/pfr/detail/core_name14_disabled.hpp @@ -17,23 +17,23 @@ namespace boost { namespace pfr { namespace detail { -template -constexpr auto get_name() noexcept { +template +constexpr void report_name_reflection_mising_requirement() noexcept { static_assert( sizeof(T) && false, "====================> Boost.PFR: Field's names extracting functionality requires C++20." ); +} +template +constexpr auto get_name() noexcept { + detail::report_name_reflection_mising_requirement(); return nullptr; } template constexpr auto tie_as_names_tuple() noexcept { - static_assert( - sizeof(T) && false, - "====================> Boost.PFR: Field's names extracting functionality requires C++20." - ); - + detail::report_name_reflection_mising_requirement(); return detail::sequence_tuple::make_sequence_tuple(); } From 6732b138ab03dcd374b4c01f6869698ed397e6da Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sun, 1 Feb 2026 21:00:17 +0300 Subject: [PATCH 12/13] update --- include/boost/pfr/config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/pfr/config.hpp b/include/boost/pfr/config.hpp index 3f02bbb2..a3f725a1 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -127,7 +127,7 @@ || (defined(__clang_major__) && __clang_major__ >= 12) # define BOOST_PFR_CORE_NAME_ENABLED 1 # else -# define BOOST_PFR_CORE_NAME_ENABLED 0 +# define BOOST_PFR_CORE_NAME_ENABLED BOOST_PFR_USE_CPP26_REFLECTION # endif # else # define BOOST_PFR_CORE_NAME_ENABLED 0 From 8f9943c4047b69f69bc388dec2774cddd61eab51 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Sun, 1 Feb 2026 21:01:43 +0300 Subject: [PATCH 13/13] typo --- include/boost/pfr/detail/core_name26_reflection.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/pfr/detail/core_name26_reflection.hpp b/include/boost/pfr/detail/core_name26_reflection.hpp index 6af5221b..d0a216e1 100644 --- a/include/boost/pfr/detail/core_name26_reflection.hpp +++ b/include/boost/pfr/detail/core_name26_reflection.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2026 Antony Polukhin +// Copyright (c) 2025-2026 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)