diff --git a/doc/pfr.qbk b/doc/pfr.qbk index 8e1e567c..37d9df1e 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 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 == 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/config.hpp b/include/boost/pfr/config.hpp index bd92fe5a..a3f725a1 100644 --- a/include/boost/pfr/config.hpp +++ b/include/boost/pfr/config.hpp @@ -61,6 +61,15 @@ #endif #endif +#ifndef BOOST_PFR_USE_CPP26_REFLECTION +#ifdef __cpp_lib_reflection +// TODO: experimental. Not enabled by default for now +#define BOOST_PFR_USE_CPP26_REFLECTION 0 +#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 @@ -118,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 diff --git a/include/boost/pfr/core.hpp b/include/boost/pfr/core.hpp index a4f230cd..7e011aeb 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/core_name.hpp b/include/boost/pfr/core_name.hpp index 186d29be..9196a94a 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 @@ -106,7 +107,24 @@ 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)); +#if BOOST_PFR_CORE_NAME_ENABLED + return boost::pfr::detail::for_each_field( + std::forward(value), + [&func](auto&& field, auto index) { + 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)); + } + }); +#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.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..d287d045 --- /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.[: + 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 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_name14_disabled.hpp b/include/boost/pfr/detail/core_name14_disabled.hpp index 9d342781..2e016056 100644 --- a/include/boost/pfr/detail/core_name14_disabled.hpp +++ b/include/boost/pfr/detail/core_name14_disabled.hpp @@ -17,35 +17,26 @@ 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(); } - -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 ba03cac9..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), - [&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/include/boost/pfr/detail/core_name26_reflection.hpp b/include/boost/pfr/detail/core_name26_reflection.hpp new file mode 100644 index 00000000..d0a216e1 --- /dev/null +++ b/include/boost/pfr/detail/core_name26_reflection.hpp @@ -0,0 +1,63 @@ +// 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) + + +// 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()>{}); +} + +}}} // namespace boost::pfr::detail + +#endif // BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP + diff --git a/include/boost/pfr/detail/fields_count.hpp b/include/boost/pfr/detail/fields_count.hpp index 062381e3..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__ @@ -207,7 +212,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 +349,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 { 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 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..b809bc27 --- /dev/null +++ b/test/core/cpp26_reflection_detection.cpp @@ -0,0 +1,35 @@ +// 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 C++26 Reflection + +#include +#include + +#ifndef __cpp_lib_reflection +#error Compiler does not support the required features +#endif + +#include + +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(); +} +