Skip to content
2 changes: 2 additions & 0 deletions include/boost/pfr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
#include <boost/pfr/core_name.hpp>
#include <boost/pfr/functions_for.hpp>
#include <boost/pfr/functors.hpp>
#include <boost/pfr/index_of.hpp>
#include <boost/pfr/io.hpp>
#include <boost/pfr/io_fields.hpp>
#include <boost/pfr/is_constexpr_default_constructible.hpp>
#include <boost/pfr/ops.hpp>
#include <boost/pfr/ops_fields.hpp>
#include <boost/pfr/tuple_size.hpp>
Expand Down
4 changes: 2 additions & 2 deletions include/boost/pfr/detail/for_each_field.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ constexpr void for_each_field(T&& value, F&& func) {

::boost::pfr::detail::for_each_field_dispatcher(
value,
[f = std::forward<F>(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<std::remove_reference_t<T>>();

::boost::pfr::detail::for_each_field_impl(
t,
std::forward<F>(f),
std::forward<F>(func),
detail::make_index_sequence<fields_count_val_in_lambda>{},
std::is_rvalue_reference<T&&>{}
);
Expand Down
85 changes: 85 additions & 0 deletions include/boost/pfr/index_of.hpp
Original file line number Diff line number Diff line change
@@ -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 <boost/pfr/detail/config.hpp>

#if !defined(BOOST_USE_MODULES) || defined(BOOST_PFR_INTERFACE_UNIT)

#include <boost/pfr/detail/for_each_field.hpp>
#include <boost/pfr/is_constexpr_default_constructible.hpp>


#if !defined(BOOST_PFR_INTERFACE_UNIT)
#include <type_traits>
#include <memory> // std::addressof
#endif

/// \file boost/pfr/index_of.hpp
/// \copybrief boost::pfr::index_of

namespace boost { namespace pfr {

namespace detail {

template <typename M>
struct address_comparing_visitor {
template <class T>
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 <typename T, typename M>
constexpr std::size_t index_of(M T::*mem_ptr, const T& value) noexcept {
if (mem_ptr == nullptr) {
return ~static_cast<std::size_t>(0);
}

detail::address_comparing_visitor<M> visitor{
std::addressof(value.*mem_ptr),
~static_cast<std::size_t>(0)
};
::boost::pfr::detail::for_each_field(value, visitor);

return visitor.result;
}

template <typename T, typename M>
constexpr std::size_t index_of(M T::*mem_ptr) noexcept {
static_assert(
boost::pfr::is_constexpr_default_constructible_v<T>,
"====================> 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<T> 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
49 changes: 49 additions & 0 deletions include/boost/pfr/is_constexpr_default_constructible.hpp
Original file line number Diff line number Diff line change
@@ -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 <boost/pfr/detail/config.hpp>

#if !defined(BOOST_USE_MODULES) || defined(BOOST_PFR_INTERFACE_UNIT)

#if !defined(BOOST_PFR_INTERFACE_UNIT)
#include <type_traits>
#endif

namespace boost { namespace pfr {

namespace detail {
template <class T, int = (static_cast<void>(T{}), 0)>
constexpr std::true_type is_constexpr_default_constructible(long) noexcept {
return {};
}

template <class T>
constexpr std::false_type is_constexpr_default_constructible(int) noexcept {
return {};
}
} // namespace detail

BOOST_PFR_BEGIN_MODULE_EXPORT

template <class T>
struct is_constexpr_default_constructible
: decltype(detail::is_constexpr_default_constructible<T>(1L))
{};

template <class T>
constexpr bool is_constexpr_default_constructible_v
= decltype(detail::is_constexpr_default_constructible<T>(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
41 changes: 41 additions & 0 deletions test/core/run/is_constexpr_constructible.cpp
Original file line number Diff line number Diff line change
@@ -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 <boost/pfr/is_constexpr_default_constructible.hpp>

#include <string>

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<Yes>, "");
static_assert(is_constexpr_default_constructible_v<Yes2>, "");
static_assert(!is_constexpr_default_constructible_v<No>, "");
}
58 changes: 58 additions & 0 deletions test/core/run/member_to_index.cpp
Original file line number Diff line number Diff line change
@@ -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 <boost/pfr/index_of.hpp>

#include <limits>
#include <string>
#include <vector>

struct Sample {
std::string x;
std::vector<int> y;
std::string z;
};

struct TrivialSample {
int x;
short y;
bool z;
};

template <class T>
int test_if_constexpr_default_constructible(std::false_type) {
return 0;
}

template <class T>
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<std::size_t>::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<Sample>(
boost::pfr::is_constexpr_default_constructible<Sample>{}
);
}
Loading