Skip to content

Commit

Permalink
Add function for_each_field_with_name
Browse files Browse the repository at this point in the history
To be able to iterate over the fields name and value of an aggregate
  • Loading branch information
Baduit committed Aug 27, 2024
1 parent e1e908e commit c203197
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 1 deletion.
3 changes: 3 additions & 0 deletions doc/pfr.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ Boost.PFR is a header only library that does not depend on Boost. You can just c

[funcref boost::pfr::io]
]
][
[ [pfr_quick_examples_for_each_with_name] ]
[ [funcref boost::pfr::for_each_field_with_name] ]
][
[ [pfr_quick_examples_functions_for] ]
[ [macroref BOOST_PFR_FUNCTIONS_FOR] ]
Expand Down
29 changes: 29 additions & 0 deletions example/quick_examples.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,35 @@ void test_examples() {
}


// Disabling for MSVC as it gives a hard error on using local types:
//
// error C7631:
// 'boost::pfr::detail::do_not_use_PFR_with_local_types<test_examples::sample>':
// variable with internal linkage declared but not defined
#if BOOST_PFR_CORE_NAME_ENABLED && BOOST_PFR_USE_CPP17 && !defined(_MSC_VER)
{
//[pfr_quick_examples_for_each_with_name
// Print the name and value
// of each element of the structure

struct test {
int n;
std::string str;
};

test var{42, "Hello, World!"};

// Outputs:
// n: 42
// str: Hello, World!
boost::pfr::for_each_field_with_name(var,
[](std::string_view name, const auto& value) {
std::cout << name << ": " << value << std::endl;
});
//]
}
#endif

{
//[pfr_quick_examples_tuple_size
// Getting fields count of some structure
Expand Down
23 changes: 22 additions & 1 deletion include/boost/pfr/core_name.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,30 @@ names_as_array() noexcept {
);
}


/// Calls `func` for each field with its name of a `value`
///
/// \param func must have one of the following signatures:
/// * any_return_type func(std::string_view name, U&& field) // field of value is perfect forwarded to function
/// * any_return_type func(std::string_view name, U&& field, std::size_t i)
/// * any_return_type func(std::string_view name, U&& value, I i) // Here I is an `std::integral_constant<size_t, field_index>`
///
/// \param value To each field of this variable will be the `func` applied.
///
/// \b Example:
/// \code
/// struct Toto { int a; char c; };
/// Toto t {5, 'c'};
/// auto print = [](std::string_view name, const auto& value){ std::cout << "Name: " << name << " Value: " << value << std::endl; };
/// for_each_field_with_name(t, print);
/// \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));
}

BOOST_PFR_END_MODULE_EXPORT

}} // namespace boost::pfr

#endif // BOOST_PFR_CORE_NAME_HPP

9 changes: 9 additions & 0 deletions include/boost/pfr/detail/core_name14_disabled.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ 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
Expand Down
17 changes: 17 additions & 0 deletions include/boost/pfr/detail/core_name20_static.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#define BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP
#pragma once

#include <boost/pfr/core.hpp>
#include <boost/pfr/detail/config.hpp>
#include <boost/pfr/detail/core.hpp>
#include <boost/pfr/detail/sequence_tuple.hpp>
Expand Down Expand Up @@ -241,6 +242,22 @@ 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::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));
}
});
}

}}} // namespace boost::pfr::detail

#endif // BOOST_PFR_DETAIL_CORE_NAME20_STATIC_HPP
Expand Down
31 changes: 31 additions & 0 deletions test/core_name/run/for_each_field_with_name.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2016-2024 Lena Bertho
//
// 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 <map>
#include <string>

#include <boost/pfr/core_name.hpp>
#include <boost/core/lightweight_test.hpp>


struct SimpleStruct {
char c;
std::string str;
};


int main () {
std::map<std::string, std::string> m;
auto fill = [&m](std::string_view name, const auto& value){
m[std::string(name)] = value;
};

boost::pfr::for_each_field_with_name(SimpleStruct{ 'e', "test"}, fill);
BOOST_TEST_EQ(m.size(), 2);
BOOST_TEST_EQ(m["c"], "e");
BOOST_TEST_EQ(m["str"], "test");

return boost::report_errors();
}

0 comments on commit c203197

Please sign in to comment.