Skip to content

Commit

Permalink
Merge pull request #59 from grandseiken/main
Browse files Browse the repository at this point in the history
add rfl::NamedTuple::apply() for convenient compile-time iteration
  • Loading branch information
liuzicheng1987 authored Feb 8, 2024
2 parents 70b8c0b + 34c36d9 commit c1e5875
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 1 deletion.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ for (const auto& f : rfl::fields<Person>()) {
}
```
You can also create a view and then access these fields using `std::get` or `rfl::get`:
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:
```cpp
auto lisa = Person{.first_name = "Lisa", .last_name = "Simpson", .age = 8};
Expand All @@ -298,6 +298,11 @@ const auto view = rfl::to_view(lisa);
*view.get<"age">() = 0;
*rfl::get<0>(view) = "Maggie";
*rfl::get<"first_name">(view) = "Maggie";
view.apply([](const auto& f) {
// f is a an rfl::Field pointing to the original field.
std::cout << f.name() << ": " << rfl::json::write(*f.value()) << std::endl;
});
```

It also possible to replace fields:
Expand Down
19 changes: 19 additions & 0 deletions docs/named_tuple.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,25 @@ const auto first_name = person.template get<"firstName">();
const auto first_name = rfl::get<"firstName">(person);
```

Fields can also be iterated over at compile-time using the `apply()` method:

```cpp
auto person = rfl::Field<"first_name", std::string>("Bart") *
rfl::Field<"last_name", std::string>("Simpson");

person.apply([](const auto& f) {
auto field_name = f.name();
const auto& value = *f.value();
});

person.apply([]<typename Field>(Field& f) {
// The field name can also be obtained as a compile-time constant.
constexpr auto field_name = Field::name();
using field_pointer_type = typename Field::Type;
field_pointer_type* value = f.value();
});
```

### `rfl::replace`

`rfl::replace` works for `rfl::NamedTuple` as well:
Expand Down
3 changes: 3 additions & 0 deletions include/rfl/Field.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ struct Field {
/// The name of the field, for internal use.
constexpr static const internal::StringLiteral name_ = _name;

/// The name of the field.
constexpr static std::string_view name() { return name_.string_view(); }

/// Returns the underlying object.
const Type& get() const { return value_; }

Expand Down
20 changes: 20 additions & 0 deletions include/rfl/NamedTuple.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,26 @@ class NamedTuple {
return rfl::make_field<_field_name>(rfl::get<_field_name>(*this));
}

/// Invokes a callable object once for each field in order.
template <typename F>
void apply(F&& f) {
std::apply(
[&f]<typename... AFields>(AFields&&... fields) {
((f(std::forward<AFields>(fields))), ...);
},
fields());
}

/// Invokes a callable object once for each field in order.
template <typename F>
void apply(F&& f) const {
std::apply(
[&f]<typename... AFields>(AFields&&... fields) {
((f(std::forward<AFields>(fields))), ...);
},
fields());
}

/// Copy assignment operator.
NamedTuple<FieldTypes...>& operator=(
const NamedTuple<FieldTypes...>& _other) = default;
Expand Down
19 changes: 19 additions & 0 deletions tests/json/test_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <rfl/json.hpp>
#include <source_location>
#include <string>
#include <type_traits>
#include <vector>

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

write_and_read(lisa,
R"({"first_name":"Maggie","last_name":"Simpson","age":0})");

view.apply([]<typename Field>(const Field& field) {
using field_type = std::remove_pointer_t<typename Field::Type>;
if constexpr (Field::name() == "age") {
static_assert(std::is_same_v<field_type, int>);
} else {
static_assert(std::is_same_v<field_type, std::string>);
}
});

rfl::to_view(lisa).apply([](auto field) {
if constexpr (decltype(field)::name() == "first_name") {
*field.value() = "Bart";
}
});

write_and_read(lisa,
R"({"first_name":"Bart","last_name":"Simpson","age":0})");
}
} // namespace test_view

0 comments on commit c1e5875

Please sign in to comment.