From 8383f5e20463f655ddae0e01b7535da53ecbe1f3 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Mon, 5 Jan 2026 23:18:42 +0300 Subject: [PATCH] Member pointer to index (WIP) --- include/boost/pfr/core.hpp | 56 +++++++++++++++++++++++++++++++ test/core/run/member_to_index.cpp | 26 ++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 test/core/run/member_to_index.cpp diff --git a/include/boost/pfr/core.hpp b/include/boost/pfr/core.hpp index d621f27..0a88cfa 100644 --- a/include/boost/pfr/core.hpp +++ b/include/boost/pfr/core.hpp @@ -18,9 +18,11 @@ #include #include #include +#include #include + #if !defined(BOOST_PFR_INTERFACE_UNIT) #include #include // metaprogramming stuff @@ -277,6 +279,60 @@ constexpr detail::tie_from_structure_tuple tie_from_structure(Eleme return detail::tie_from_structure_tuple(args...); } +template +constexpr std::size_t index_of(const T& value, M T::*mem_ptr) { + constexpr auto size = boost::pfr::tuple_size_v; + std::size_t result = size; + + const void* target_address = std::addressof(value.*mem_ptr); + + boost::pfr::for_each_field(value, [&result, target_address](const auto& field, std::size_t idx) { + if (!std::is_same::value) { + return; + } + + if (result != size) { + // already found the answer + return; + } + + const void* filed_address = std::addressof(field); + if (target_address == filed_address) { + result = idx; + } + }); + + return result; +} + +// TODO: move into detail:: +template +auto strip_references(detail::sequence_tuple::tuple) -> detail::sequence_tuple::tuple; + +template +std::size_t index_of(M T::*mem_ptr) { + using tuple_type = detail::tuple_of_aligned_storage_t< + decltype(strip_references(detail::tie_as_tuple(std::declval()))) + >; + using converted_member_pointer_t = M tuple_type::*; + + constexpr tuple_type t{}; + + // TODO: not allowed in constexpr + unspecified behavior + auto mem_pointer = reinterpret_cast(mem_ptr); + + const void* pointer = std::addressof(t.*mem_pointer); + namespace sequence_tuple = boost::pfr::detail::sequence_tuple; + if (&sequence_tuple::get<0>(t) == pointer) { + return 0; + } else if (&sequence_tuple::get<1>(t) == pointer) { + return 1; + } else if (&sequence_tuple::get<2>(t) == pointer) { + return 2; + } + return 3; +} + BOOST_PFR_END_MODULE_EXPORT }} // namespace boost::pfr diff --git a/test/core/run/member_to_index.cpp b/test/core/run/member_to_index.cpp new file mode 100644 index 0000000..94deacb --- /dev/null +++ b/test/core/run/member_to_index.cpp @@ -0,0 +1,26 @@ +// 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; +}; + +int main() { + assert(boost::pfr::index_of(&Sample::x) == 0); + assert(boost::pfr::index_of(&Sample::y) == 1); + assert(boost::pfr::index_of(&Sample::z) == 2); + + static_assert(boost::pfr::index_of(Sample{}, &Sample::x) == 0); + static_assert(boost::pfr::index_of(Sample{}, &Sample::y) == 1); + static_assert(boost::pfr::index_of(Sample{}, &Sample::z) == 2); +}