2
0
mirror of https://github.com/boostorg/pfr.git synced 2026-02-01 08:42:14 +00:00

Compare commits

..

9 Commits

Author SHA1 Message Date
Antony Polukhin
f3324ed1c1 Merge branch 'develop' into reflection-based-impl 2026-01-31 16:32:24 +03:00
Antony Polukhin
b12551c547 remove code duplication 2026-01-31 13:14:24 +03:00
Antony Polukhin
b7f8dfe109 fixes 2026-01-31 13:03:44 +03:00
Antony Polukhin
25b5fd744d tweaks 2026-01-31 12:59:34 +03:00
Antony Polukhin
56c12f35d5 fix 2026-01-30 14:21:31 +03:00
Antony Polukhin
c698cb4009 fixes and tweaks 2026-01-30 13:54:16 +03:00
Antony Polukhin
bad6129b56 Implement core in reflection 2026-01-30 13:16:14 +03:00
Antony Polukhin
08a78e7471 fields count via C++26 reflection 2026-01-30 12:50:46 +03:00
Antony Polukhin
114ef61060 C++26 reflection based implementation 2026-01-30 12:03:53 +03:00
15 changed files with 261 additions and 46 deletions

View File

@@ -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 <auto member_ptr>`.

View File

@@ -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

View File

@@ -52,7 +52,9 @@ BOOST_PFR_BEGIN_MODULE_EXPORT
/// \endcode
template <std::size_t I, class T>
constexpr decltype(auto) get(const T& val) noexcept {
#if BOOST_PFR_USE_CPP26
#if BOOST_PFR_USE_CPP26_REFLECTION
return detail::reference_by_index<I>(val);
#elif BOOST_PFR_USE_CPP26
const auto& [... members] = val;
return std::forward_like<const T &>(members...[I]);
#else
@@ -67,7 +69,9 @@ constexpr decltype(auto) get(T& val
, std::enable_if_t<std::is_assignable<T, T>::value>* = nullptr
#endif
) noexcept {
#if BOOST_PFR_USE_CPP26
#if BOOST_PFR_USE_CPP26_REFLECTION
return detail::reference_by_index<I>(val);
#elif BOOST_PFR_USE_CPP26
auto& [... members] = val;
return std::forward_like<T &>(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 <std::size_t I, class T>
constexpr auto get(T&, std::enable_if_t<!std::is_assignable<T, T>::value>* = nullptr) noexcept {
@@ -88,7 +92,9 @@ constexpr auto get(T&, std::enable_if_t<!std::is_assignable<T, T>::value>* = nul
/// \overload get
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
#if BOOST_PFR_USE_CPP26_REFLECTION
return std::move(detail::reference_by_index<I>(val));
#elif BOOST_PFR_USE_CPP26
auto&& [... members] = std::forward<T>(val);
return std::move(members...[I]);
#else
@@ -107,14 +113,14 @@ 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 && !BOOST_PFR_USE_CPP26
#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26 && !BOOST_PFR_USE_CPP26_REFLECTION
, 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 && !BOOST_PFR_USE_CPP26
#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26 && !BOOST_PFR_USE_CPP26_REFLECTION
/// \overload get
template <class U, class T>
constexpr U& get(T&, std::enable_if_t<!std::is_assignable<T, T>::value>* = nullptr) noexcept {
@@ -207,7 +213,7 @@ 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 && !BOOST_PFR_USE_CPP26
#if !BOOST_PFR_USE_CPP17 && !BOOST_PFR_USE_CPP26 && !BOOST_PFR_USE_CPP26_REFLECTION
, std::enable_if_t<std::is_assignable<T, 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 <class T>
constexpr auto structure_tie(T&, std::enable_if_t<!std::is_assignable<T, T>::value>* = nullptr) noexcept {

View File

@@ -106,7 +106,18 @@ names_as_array() noexcept {
/// \endcode
template <class T, class F>
constexpr void for_each_field_with_name(T&& value, F&& func) {
return boost::pfr::detail::for_each_field_with_name(std::forward<T>(value), std::forward<F>(func));
return boost::pfr::detail::for_each_field(
std::forward<T>(value),
[f = std::forward<F>(func)](auto&& field, auto index) mutable {
using IndexType = decltype(index);
using FieldType = decltype(field);
constexpr auto name = boost::pfr::detail::get_name<std::remove_reference_t<T>, IndexType::value>();
if constexpr (std::is_invocable_v<F, std::string_view, FieldType, IndexType>) {
f(name, std::forward<FieldType>(field), index);
} else {
f(name, std::forward<FieldType>(field));
}
});
}
BOOST_PFR_END_MODULE_EXPORT

View File

@@ -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 <boost/pfr/detail/core26_reflection.hpp>
#elif BOOST_PFR_USE_CPP26
#include <boost/pfr/detail/core26.hpp>
#elif BOOST_PFR_USE_CPP17
# include <boost/pfr/detail/core17.hpp>

View File

@@ -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 <boost/pfr/detail/config.hpp>
#include <boost/pfr/detail/fields_count.hpp>
#include <boost/pfr/detail/make_integer_sequence.hpp>
#include <boost/pfr/detail/sequence_tuple.hpp>
#if !defined(BOOST_PFR_INTERFACE_UNIT)
#include <type_traits>
#include <meta>
#endif
namespace boost::pfr::detail {
template <std::size_t I, typename T>
consteval decltype(auto) reference_by_index(T &val) noexcept {
return val.[:
nonstatic_data_members_of(
^^T,
std::meta::access_context::current()
).at(I)
:];
}
template<class T, std::size_t... I>
constexpr auto tie_as_tuple_impl(T &val, std::index_sequence<I...>) noexcept {
return sequence_tuple::tuple<
decltype(boost::pfr::detail::reference_by_index<I>(val))...
>{
boost::pfr::detail::reference_by_index<I>(val)...
};
}
template<class T>
constexpr auto tie_as_tuple(T &val) noexcept {
return detail::tie_as_tuple_impl(
val,
detail::make_index_sequence<detail::fields_count<T>()>{}
);
}
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

@@ -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 <boost/pfr/detail/core_name26_reflection.hpp>
#elif BOOST_PFR_CORE_NAME_ENABLED
#include <boost/pfr/detail/core_name20_static.hpp>
#else
#include <boost/pfr/detail/core_name14_disabled.hpp>

View File

@@ -37,15 +37,6 @@ constexpr auto tie_as_names_tuple() noexcept {
return detail::sequence_tuple::make_sequence_tuple();
}
template <class T, class F>
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

View File

@@ -241,22 +241,6 @@ constexpr auto tie_as_names_tuple() noexcept {
return detail::tie_as_names_tuple_impl<T>(detail::make_index_sequence<detail::fields_count<T>()>{});
}
template <class T, class F>
constexpr void for_each_field_with_name(T&& value, F&& func) {
return boost::pfr::detail::for_each_field(
std::forward<T>(value),
[&func](auto&& field, auto index) mutable {
using IndexType = decltype(index);
using FieldType = decltype(field);
constexpr auto name = boost::pfr::detail::get_name<std::remove_reference_t<T>, IndexType::value>();
if constexpr (std::is_invocable_v<F, std::string_view, FieldType, IndexType>) {
std::forward<F>(func)(name, std::forward<FieldType>(field), index);
} else {
std::forward<F>(func)(name, std::forward<FieldType>(field));
}
});
}
}}} // namespace boost::pfr::detail
#endif // BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP

View File

@@ -0,0 +1,63 @@
// 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 <boost/pfr/detail/config.hpp>
#include <boost/pfr/detail/fields_count.hpp>
#include <boost/pfr/detail/for_each_field.hpp>
#include <boost/pfr/detail/make_integer_sequence.hpp>
#include <boost/pfr/detail/sequence_tuple.hpp>
#if !defined(BOOST_PFR_INTERFACE_UNIT)
#include <type_traits>
#include <array>
#include <memory> // for std::addressof
#include <meta>
#endif
namespace boost { namespace pfr { namespace detail {
template <class T, std::size_t I>
constexpr std::string_view get_name() 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."
);
static_assert(
!std::is_array<T>::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 <class T, std::size_t... I>
constexpr auto tie_as_names_tuple_impl(std::index_sequence<I...>) noexcept {
return detail::sequence_tuple::make_sequence_tuple(detail::get_name<T, I>()...);
}
template <class T>
constexpr auto tie_as_names_tuple() noexcept {
return detail::tie_as_names_tuple_impl<T>(detail::make_index_sequence<detail::fields_count<T>()>{});
}
}}} // namespace boost::pfr::detail
#endif // BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP

View File

@@ -16,6 +16,11 @@
#include <limits>
#include <type_traits>
#include <utility> // metaprogramming stuff
#if BOOST_PFR_USE_CPP26_REFLECTION
#include <meta>
#endif
#endif
#ifdef __clang__
@@ -207,7 +212,7 @@ using is_one_element_range = std::integral_constant<bool, Begin == Last>;
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<T>);
}
#if BOOST_PFR_USE_CPP26
#if BOOST_PFR_USE_CPP26_REFLECTION
template<class T>
constexpr auto fields_count_dispatch_impl(const T &) noexcept
{
return std::integral_constant<std::size_t,
nonstatic_data_members_of(
^^T,
std::meta::access_context::current()
).size()
>{};
}
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;
}
#elif BOOST_PFR_USE_CPP26
template<class T>
constexpr auto fields_count_dispatch_impl(const T &t) noexcept
{

View File

@@ -28,6 +28,11 @@ import std;
#include <type_traits>
#include <utility>
#include <variant>
#ifdef __cpp_lib_reflection
#include <meta>
#endif
#endif
#define BOOST_PFR_INTERFACE_UNIT

View File

@@ -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'

View File

@@ -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 : : <build>no ]
;
local REQUIRE_CPP26_REFLECTION =
[ check-target-builds ../core//compiler_supports_cpp26_reflection : : <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 ] ;
local LOOPHOLE_ENGINE = <define>BOOST_PFR_USE_LOOPHOLE=1 <define>BOOST_PFR_USE_CPP17=0 <define>BOOST_PFR_USE_CPP26=0 $(REQUIRE_LOOPHOLE) ;
local CLASSIC_ENGINE = <define>BOOST_PFR_USE_LOOPHOLE=0 <define>BOOST_PFR_USE_CPP17=0 <define>BOOST_PFR_USE_CPP26=0 $(DISABLE_ON_MSVC) ;
local CPP26_REFLECTION_ENGINE = <define>BOOST_PFR_USE_LOOPHOLE=0 <define>BOOST_PFR_USE_CPP17=0 <define>BOOST_PFR_USE_CPP26=0 <define>BOOST_PFR_USE_CPP26_REFLECTION=1 $(REQUIRE_CPP26_REFLECTION) ;
local VARIADIC_STRUCTURED_BINDING_ENGINE = <define>BOOST_PFR_USE_LOOPHOLE=0 <define>BOOST_PFR_USE_CPP17=0 <define>BOOST_PFR_USE_CPP26=1 <define>BOOST_PFR_USE_CPP26_REFLECTION=0 $(REQUIRE_VSB) ;
local STRUCTURED_BINDING_ENGINE = <define>BOOST_PFR_USE_LOOPHOLE=0 <define>BOOST_PFR_USE_CPP17=1 <define>BOOST_PFR_USE_CPP26=0 <define>BOOST_PFR_USE_CPP26_REFLECTION=0 [ requires cxx17_structured_bindings ] ;
local LOOPHOLE_ENGINE = <define>BOOST_PFR_USE_LOOPHOLE=1 <define>BOOST_PFR_USE_CPP17=0 <define>BOOST_PFR_USE_CPP26=0 <define>BOOST_PFR_USE_CPP26_REFLECTION=0 $(REQUIRE_LOOPHOLE) ;
local CLASSIC_ENGINE = <define>BOOST_PFR_USE_LOOPHOLE=0 <define>BOOST_PFR_USE_CPP17=0 <define>BOOST_PFR_USE_CPP26=0 <define>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 ] ;

View File

@@ -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 <type_traits>
#include <version>
#ifndef __cpp_lib_reflection
#error Compiler does not support the required features
#endif
#include <meta>
struct MyPair {
int first;
int second;
};
template <class T>
auto test() {
return T{}.[:
nonstatic_data_members_of(
^^T,
std::meta::access_context::current()
).at(0)
:];
}
int main() {
return ::test<MyPair>();
}