Skip to content

Commit c1e5875

Browse files
Merge pull request #59 from grandseiken/main
add rfl::NamedTuple::apply() for convenient compile-time iteration
2 parents 70b8c0b + 34c36d9 commit c1e5875

File tree

5 files changed

+67
-1
lines changed

5 files changed

+67
-1
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ for (const auto& f : rfl::fields<Person>()) {
281281
}
282282
```
283283
284-
You can also create a view and then access these fields using `std::get` or `rfl::get`:
284+
You can also create a view and then access these fields using `std::get` or `rfl::get`, or iterate over the fields at compile-time:
285285
286286
```cpp
287287
auto lisa = Person{.first_name = "Lisa", .last_name = "Simpson", .age = 8};
@@ -298,6 +298,11 @@ const auto view = rfl::to_view(lisa);
298298
*view.get<"age">() = 0;
299299
*rfl::get<0>(view) = "Maggie";
300300
*rfl::get<"first_name">(view) = "Maggie";
301+
302+
view.apply([](const auto& f) {
303+
// f is a an rfl::Field pointing to the original field.
304+
std::cout << f.name() << ": " << rfl::json::write(*f.value()) << std::endl;
305+
});
301306
```
302307

303308
It also possible to replace fields:

docs/named_tuple.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,25 @@ const auto first_name = person.template get<"firstName">();
9595
const auto first_name = rfl::get<"firstName">(person);
9696
```
9797

98+
Fields can also be iterated over at compile-time using the `apply()` method:
99+
100+
```cpp
101+
auto person = rfl::Field<"first_name", std::string>("Bart") *
102+
rfl::Field<"last_name", std::string>("Simpson");
103+
104+
person.apply([](const auto& f) {
105+
auto field_name = f.name();
106+
const auto& value = *f.value();
107+
});
108+
109+
person.apply([]<typename Field>(Field& f) {
110+
// The field name can also be obtained as a compile-time constant.
111+
constexpr auto field_name = Field::name();
112+
using field_pointer_type = typename Field::Type;
113+
field_pointer_type* value = f.value();
114+
});
115+
```
116+
98117
### `rfl::replace`
99118

100119
`rfl::replace` works for `rfl::NamedTuple` as well:

include/rfl/Field.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ struct Field {
6262
/// The name of the field, for internal use.
6363
constexpr static const internal::StringLiteral name_ = _name;
6464

65+
/// The name of the field.
66+
constexpr static std::string_view name() { return name_.string_view(); }
67+
6568
/// Returns the underlying object.
6669
const Type& get() const { return value_; }
6770

include/rfl/NamedTuple.hpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,26 @@ class NamedTuple {
210210
return rfl::make_field<_field_name>(rfl::get<_field_name>(*this));
211211
}
212212

213+
/// Invokes a callable object once for each field in order.
214+
template <typename F>
215+
void apply(F&& f) {
216+
std::apply(
217+
[&f]<typename... AFields>(AFields&&... fields) {
218+
((f(std::forward<AFields>(fields))), ...);
219+
},
220+
fields());
221+
}
222+
223+
/// Invokes a callable object once for each field in order.
224+
template <typename F>
225+
void apply(F&& f) const {
226+
std::apply(
227+
[&f]<typename... AFields>(AFields&&... fields) {
228+
((f(std::forward<AFields>(fields))), ...);
229+
},
230+
fields());
231+
}
232+
213233
/// Copy assignment operator.
214234
NamedTuple<FieldTypes...>& operator=(
215235
const NamedTuple<FieldTypes...>& _other) = default;

tests/json/test_view.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <rfl/json.hpp>
55
#include <source_location>
66
#include <string>
7+
#include <type_traits>
78
#include <vector>
89

910
#include "test_replace.hpp"
@@ -30,5 +31,23 @@ void test() {
3031

3132
write_and_read(lisa,
3233
R"({"first_name":"Maggie","last_name":"Simpson","age":0})");
34+
35+
view.apply([]<typename Field>(const Field& field) {
36+
using field_type = std::remove_pointer_t<typename Field::Type>;
37+
if constexpr (Field::name() == "age") {
38+
static_assert(std::is_same_v<field_type, int>);
39+
} else {
40+
static_assert(std::is_same_v<field_type, std::string>);
41+
}
42+
});
43+
44+
rfl::to_view(lisa).apply([](auto field) {
45+
if constexpr (decltype(field)::name() == "first_name") {
46+
*field.value() = "Bart";
47+
}
48+
});
49+
50+
write_and_read(lisa,
51+
R"({"first_name":"Bart","last_name":"Simpson","age":0})");
3352
}
3453
} // namespace test_view

0 commit comments

Comments
 (0)