diff --git a/include/boost/pfr.hpp b/include/boost/pfr.hpp index ce447256..8f51ce84 100644 --- a/include/boost/pfr.hpp +++ b/include/boost/pfr.hpp @@ -14,8 +14,10 @@ #include #include #include +#include #include #include +#include #include #include #include diff --git a/include/boost/pfr/detail/for_each_field.hpp b/include/boost/pfr/detail/for_each_field.hpp index 33b3ec1f..c2d92ed3 100644 --- a/include/boost/pfr/detail/for_each_field.hpp +++ b/include/boost/pfr/detail/for_each_field.hpp @@ -42,14 +42,14 @@ constexpr void for_each_field(T&& value, F&& func) { ::boost::pfr::detail::for_each_field_dispatcher( value, - [f = std::forward(func)](auto&& t) mutable { + [&func](auto&& t) mutable { // MSVC related workaround. Its lambdas do not capture constexprs. constexpr std::size_t fields_count_val_in_lambda = boost::pfr::detail::fields_count>(); ::boost::pfr::detail::for_each_field_impl( t, - std::forward(f), + std::forward(func), detail::make_index_sequence{}, std::is_rvalue_reference{} ); diff --git a/include/boost/pfr/index_of.hpp b/include/boost/pfr/index_of.hpp new file mode 100644 index 00000000..df994e4e --- /dev/null +++ b/include/boost/pfr/index_of.hpp @@ -0,0 +1,85 @@ +// 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_INDEX_OF_HPP +#define BOOST_PFR_INDEX_OF_HPP +#pragma once + +#include + +#if !defined(BOOST_USE_MODULES) || defined(BOOST_PFR_INTERFACE_UNIT) + +#include +#include + + +#if !defined(BOOST_PFR_INTERFACE_UNIT) +#include +#include // std::addressof +#endif + +/// \file boost/pfr/index_of.hpp +/// \copybrief boost::pfr::index_of + +namespace boost { namespace pfr { + +namespace detail { + +template +struct address_comparing_visitor { + template + constexpr void operator()(const T&, std::size_t) noexcept {} + + constexpr void operator()(const M& field, std::size_t idx) noexcept { + const void* field_address = std::addressof(field); + if (target_address == field_address) { + result = idx; + } + } + + const void* const target_address; + std::size_t result; +}; + +} + +BOOST_PFR_BEGIN_MODULE_EXPORT + +template +constexpr std::size_t index_of(M T::*mem_ptr, const T& value) noexcept { + if (mem_ptr == nullptr) { + return ~static_cast(0); + } + + detail::address_comparing_visitor visitor{ + std::addressof(value.*mem_ptr), + ~static_cast(0) + }; + ::boost::pfr::detail::for_each_field(value, visitor); + + return visitor.result; +} + +template +constexpr std::size_t index_of(M T::*mem_ptr) noexcept { + static_assert( + boost::pfr::is_constexpr_default_constructible_v, + "====================> Boost.PFR: T should be default constructible in constant evaluations. " + "Either add `constexpr` to constructors of member types or use the boost::pfr::index_of() overload " + "with explicit `const T& value` parameter." + ); + + // boost::pfr::is_constexpr_default_constructible_v gives a faint hope that the + // compiler would be able to optimize away the temporary `T{}`. + return ::boost::pfr::index_of(mem_ptr, T{}); +} + +BOOST_PFR_END_MODULE_EXPORT + +}} // namespace boost::pfr + +#endif // #if !defined(BOOST_USE_MODULES) || defined(BOOST_PFR_INTERFACE_UNIT) + +#endif // BOOST_PFR_INDEX_OF_HPP diff --git a/include/boost/pfr/is_constexpr_default_constructible.hpp b/include/boost/pfr/is_constexpr_default_constructible.hpp new file mode 100644 index 00000000..05976d14 --- /dev/null +++ b/include/boost/pfr/is_constexpr_default_constructible.hpp @@ -0,0 +1,49 @@ +// 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_IS_CONSTEXPR_DEFAULT_CONSTRUCTIBLE_HPP +#define BOOST_PFR_IS_CONSTEXPR_DEFAULT_CONSTRUCTIBLE_HPP +#pragma once + +#include + +#if !defined(BOOST_USE_MODULES) || defined(BOOST_PFR_INTERFACE_UNIT) + +#if !defined(BOOST_PFR_INTERFACE_UNIT) +#include +#endif + +namespace boost { namespace pfr { + +namespace detail { + template (T{}), 0)> + constexpr std::true_type is_constexpr_default_constructible(long) noexcept { + return {}; + } + + template + constexpr std::false_type is_constexpr_default_constructible(int) noexcept { + return {}; + } +} // namespace detail + +BOOST_PFR_BEGIN_MODULE_EXPORT + +template +struct is_constexpr_default_constructible + : decltype(detail::is_constexpr_default_constructible(1L)) +{}; + +template +constexpr bool is_constexpr_default_constructible_v + = decltype(detail::is_constexpr_default_constructible(1L))::value; + +BOOST_PFR_END_MODULE_EXPORT + +}} // namespace boost::pfr + +#endif // #if !defined(BOOST_USE_MODULES) || defined(BOOST_PFR_INTERFACE_UNIT) + +#endif // BOOST_PFR_IS_CONSTEXPR_DEFAULT_CONSTRUCTIBLE_HPP diff --git a/test/core/run/is_constexpr_constructible.cpp b/test/core/run/is_constexpr_constructible.cpp new file mode 100644 index 00000000..5a22bf6e --- /dev/null +++ b/test/core/run/is_constexpr_constructible.cpp @@ -0,0 +1,41 @@ +// 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) + +#include + +#include + +struct Yes { + int a; + double b; + char c; +}; + +struct Yes2 { + int a; + double b; + char c; + Yes d[42]; +}; + +class MyClass{ +public: + MyClass(); + ~MyClass(); +}; + +struct No { + int a; + double b; + char c; + std::string str; + MyClass mc; +}; + +int main() { + static_assert(is_constexpr_default_constructible_v, ""); + static_assert(is_constexpr_default_constructible_v, ""); + static_assert(!is_constexpr_default_constructible_v, ""); +} diff --git a/test/core/run/member_to_index.cpp b/test/core/run/member_to_index.cpp new file mode 100644 index 00000000..694c9da4 --- /dev/null +++ b/test/core/run/member_to_index.cpp @@ -0,0 +1,58 @@ +// 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) + +#include + +#include +#include +#include + +struct Sample { + std::string x; + std::vector y; + std::string z; +}; + +struct TrivialSample { + int x; + short y; + bool z; +}; + +template +int test_if_constexpr_default_constructible(std::false_type) { + return 0; +} + +template +int test_if_constexpr_default_constructible(std::true_type) { + if (boost::pfr::index_of(&T::x) != 0) return 31; + if (boost::pfr::index_of(&T::y) != 1) return 32; + if (boost::pfr::index_of(&T::z) != 2) return 33; + return 0; +} + +int main() { +#if BOOST_PFR_USE_CPP17 || BOOST_PFR_USE_CPP26 + static_assert(boost::pfr::index_of(&TrivialSample::x) == 0); + static_assert(boost::pfr::index_of(&TrivialSample::y) == 1); + static_assert(boost::pfr::index_of(&TrivialSample::z) == 2); +#endif + + if (boost::pfr::index_of(&Sample::x, {}) != 0) return 1; + if (boost::pfr::index_of(&Sample::y, {}) != 1) return 2; + if (boost::pfr::index_of(&Sample::z, {}) != 2) return 3; + + decltype(&Sample::x) mem_ptr = nullptr; + if (boost::pfr::index_of(mem_ptr, {}) != (std::numeric_limits::max)()) return 4; + + if (boost::pfr::index_of(&TrivialSample::x) != 0) return 11; + if (boost::pfr::index_of(&TrivialSample::y) != 1) return 12; + if (boost::pfr::index_of(&TrivialSample::z) != 2) return 13; + + return test_if_constexpr_default_constructible( + boost::pfr::is_constexpr_default_constructible{} + ); +}