diff --git a/doc/pfr.qbk b/doc/pfr.qbk index 37d9df1e..57bd23be 100644 --- a/doc/pfr.qbk +++ b/doc/pfr.qbk @@ -233,6 +233,9 @@ Boost.PFR is a header only library that does not depend on Boost. You can just c ][ [ [pfr_quick_examples_for_each_with_name] ] [ [funcref boost::pfr::for_each_field_with_name] ] +][ + [ [pfr_quick_examples_for_each_name] ] + [ [funcref boost::pfr::for_each_name] ] ][ [ [pfr_quick_examples_functions_for] ] [ [macroref BOOST_PFR_FUNCTIONS_FOR] ] diff --git a/example/quick_examples.cpp b/example/quick_examples.cpp index 273426b8..40c0726d 100644 --- a/example/quick_examples.cpp +++ b/example/quick_examples.cpp @@ -102,6 +102,26 @@ void test_examples() { [](std::string_view name, const auto& value) { std::cout << name << ": " << value << std::endl; }); +//] + } + + { +//[pfr_quick_examples_for_each_name + // Iterate over field names and indices of a structure + // without constructing an instance of it. + + struct sample { + int f_int; + long f_long; + }; + + // Outputs: + // 0: f_int + // 1: f_long + boost::pfr::for_each_name( + [](std::string_view name, auto index) { + std::cout << '\n' << index() << ": " << name; + }); //] } #endif diff --git a/include/boost/pfr/core_name.hpp b/include/boost/pfr/core_name.hpp index fffd1f7b..4ac78760 100644 --- a/include/boost/pfr/core_name.hpp +++ b/include/boost/pfr/core_name.hpp @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -130,6 +131,32 @@ constexpr void for_each_field_with_name(T&& value, F&& func) { #endif } +/// Calls `func` for each field name of the \aggregate `T`. +/// \param func must have one of the following signatures: +/// * any_return_type func(std::string_view name) +/// * any_return_type func(std::string_view name, I i) // Here I is an `std::integral_constant` +/// +/// \b Example: +/// \code +/// struct my_struct { int i; short s; }; +/// +/// boost::pfr::for_each_name([](std::string_view name, auto index) { +/// std::cout << index() << ": " << name << '\n'; // Outputs: 0: i 1: s +/// }); +/// \endcode +template +constexpr void for_each_name(F&& func) { +#if BOOST_PFR_CORE_NAME_ENABLED + detail::for_each_name_impl( + func, + detail::make_index_sequence< tuple_size_v >() + ); +#else + boost::pfr::detail::report_name_reflection_mising_requirement(); + (void)func; +#endif +} + BOOST_PFR_END_MODULE_EXPORT }} // namespace boost::pfr diff --git a/include/boost/pfr/detail/for_each_name.hpp b/include/boost/pfr/detail/for_each_name.hpp new file mode 100644 index 00000000..c6c18d05 --- /dev/null +++ b/include/boost/pfr/detail/for_each_name.hpp @@ -0,0 +1,39 @@ +#ifndef BOOST_PFR_DETAIL_FOR_EACH_NAME_HPP +#define BOOST_PFR_DETAIL_FOR_EACH_NAME_HPP +#pragma once + +#include + +#include + +#if BOOST_PFR_CORE_NAME_ENABLED + +#if !defined(BOOST_PFR_INTERFACE_UNIT) +#include +#include +#include +#include +#endif + +namespace boost { namespace pfr { namespace detail { + +template +constexpr void for_each_name_apply(F& func) { + constexpr std::string_view name = detail::get_name(); + if constexpr (std::is_invocable_v>) { + func(name, std::integral_constant{}); + } else { + func(name); + } +} + +template +constexpr void for_each_name_impl(F& func, std::index_sequence) { + (detail::for_each_name_apply(func), ...); +} + +}}} // namespace boost::pfr::detail + +#endif // BOOST_PFR_CORE_NAME_ENABLED + +#endif // BOOST_PFR_DETAIL_FOR_EACH_NAME_HPP diff --git a/test/core_name/run/for_each_name.cpp b/test/core_name/run/for_each_name.cpp new file mode 100644 index 00000000..2d9ea99c --- /dev/null +++ b/test/core_name/run/for_each_name.cpp @@ -0,0 +1,62 @@ +#include +#include +#include + +#include +#include + + +struct SimpleStruct { + char c; + std::string str; + int i; +}; + +struct EmptyStruct {}; + +struct stateful_counting_visitor { + std::size_t count = 0; + + void operator()(std::string_view /*name*/) { ++count; } +}; + +int main () { + std::vector names; + boost::pfr::for_each_name([&names](std::string_view name) { + names.emplace_back(name); + }); + BOOST_TEST_EQ(names.size(), 3); + BOOST_TEST_EQ(names[0], "c"); + BOOST_TEST_EQ(names[1], "str"); + BOOST_TEST_EQ(names[2], "i"); + + std::vector names_by_index(3); + boost::pfr::for_each_name([&names_by_index](std::string_view name, auto index) { + static_assert(std::is_same_v< + decltype(index), + std::integral_constant + >); + names_by_index[index] = std::string(name); + BOOST_TEST((name == boost::pfr::get_name())); + }); + BOOST_TEST_EQ(names_by_index[0], "c"); + BOOST_TEST_EQ(names_by_index[1], "str"); + BOOST_TEST_EQ(names_by_index[2], "i"); + + stateful_counting_visitor counting_visitor; + boost::pfr::for_each_name(counting_visitor); + BOOST_TEST_EQ(counting_visitor.count, 3); + + std::size_t empty_count = 0; + boost::pfr::for_each_name([&empty_count](std::string_view) { ++empty_count; }); + BOOST_TEST_EQ(empty_count, 0); + + constexpr std::size_t constexpr_count = [] { + std::size_t count = 0; + boost::pfr::for_each_name([&count](std::string_view) { ++count; }); + return count; + }(); + static_assert(constexpr_count == 3); + + return boost::report_errors(); +}