Skip to content

Commit

Permalink
Added support for rfl::to_view; #32
Browse files Browse the repository at this point in the history
  • Loading branch information
liuzicheng1987 committed Dec 24, 2023
1 parent 8ed390c commit 7de20db
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 15 deletions.
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,15 +238,30 @@ struct Person {
std::vector<Person> children;
};

const auto fields = rfl::fields<Person>();

std::cout << "Fields in " << rfl::type_name_t<Person>().str() << ":"
<< std::endl;
for (const auto& f : fields) {
for (const auto& f : rfl::fields<Person>()) {
std::cout << "name: " << f.name() << ", type: " << f.type() << std::endl;
}
```
You can also create a view and then access these fields using `std::get` or `rfl::get`:
```cpp
auto lisa = Person{.first_name = "Lisa", .last_name = "Simpson", .age = 8};
const auto view = rfl::to_view(lisa);
// view.values() is a std::tuple containing
// pointers to the original fields.
// This will modify the struct `lisa`:
*std::get<0>(view.values()) = "Maggie";
// All of this is supported as well:
*view.get<1>() = "Simpson";
*view.get<"age">() = 0;
*rfl::get<0>(view) = "Maggie";
*rfl::get<"first_name">(view) = "Maggie";
```

It also possible to replace fields:

```cpp
Expand Down
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@

4.3) [rfl::NamedTuple](https://github.com/getml/reflect-cpp/blob/main/docs/named_tuple.md) - For structural typing.

4.4) [rfl::to_view](https://github.com/getml/reflect-cpp/blob/main/docs/to_view.md) - For accessing fields of a struct by index or name.

## 5) Supported formats

5.1) [JSON](https://github.com/getml/reflect-cpp/blob/main/docs/json.md)
Expand Down
30 changes: 30 additions & 0 deletions docs/to_view.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# `rfl::to_view`

`rfl::to_view` allows you to create views on structs using which you can access an modify the fields of the structs just like a tuple.

Under-the-hood, a view is a `rfl::NamedTuple` containing pointers to the original fields.

For example:

```cpp
auto lisa = Person{.first_name = "Lisa", .last_name = "Simpson", .age = 8};

const auto view = rfl::to_view(lisa);

// Assigns the first field, thus modifying the struct 'lisa'.
*view.get<0>() = "Maggie";

// view.values() is a std::tuple containing
// pointers to the original fields.
*std::get<1>(view.values()) = "Simpson";

// You can also access fields by their name.
// The correctness will be ensured at compile time.
*view.get<"age">() = 0;

// You can also access fields like this.
*rfl::get<0>(view) = "Maggie";

// Or like this.
*rfl::get<"first_name">(view) = "Maggie";
```
1 change: 1 addition & 0 deletions include/rfl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include "rfl/remove_fields.hpp"
#include "rfl/replace.hpp"
#include "rfl/to_named_tuple.hpp"
#include "rfl/to_view.hpp"
#include "rfl/type_name_t.hpp"
#include "rfl/visit.hpp"

Expand Down
7 changes: 4 additions & 3 deletions include/rfl/internal/bind_to_tuple.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ template <class T>
constexpr auto tuple_view(T&);

template <class T, typename F>
constexpr auto bind_to_tuple(T& _t, F&& f) {
constexpr auto bind_to_tuple(T& _t, const F& _f) {
auto view = tuple_view(_t);
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return std::make_tuple(std::forward<F>(f)(std::get<Is>(view))...);
}(std::make_index_sequence<std::tuple_size_v<decltype(view)>>());
return std::make_tuple(_f(std::get<Is>(view))...);
}
(std::make_index_sequence<std::tuple_size_v<decltype(view)>>());
}

/*The following boilerplate code was generated using a Python script:
Expand Down
2 changes: 1 addition & 1 deletion include/rfl/internal/to_flattened_ptr_tuple.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ auto flatten_ptr_tuple(PtrTuple&& _t, Args... _args) {
}

template <class T>
auto to_flattened_ptr_tuple(const T& _t) {
auto to_flattened_ptr_tuple(T&& _t) {
return flatten_ptr_tuple(to_ptr_tuple(_t));
}

Expand Down
10 changes: 5 additions & 5 deletions include/rfl/internal/to_ptr_named_tuple.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace rfl {
namespace internal {

template <class PtrFieldTuple, class... Args>
auto flatten_ptr_field_tuple(const PtrFieldTuple& _t, Args&&... _args) {
auto flatten_ptr_field_tuple(PtrFieldTuple& _t, Args&&... _args) {
constexpr auto i = sizeof...(Args);
if constexpr (i == std::tuple_size_v<std::decay_t<PtrFieldTuple>>) {
return std::tuple_cat(std::forward<Args>(_args)...);
Expand All @@ -38,7 +38,7 @@ auto flatten_ptr_field_tuple(const PtrFieldTuple& _t, Args&&... _args) {
}

template <class PtrFieldTuple>
auto field_tuple_to_named_tuple(const PtrFieldTuple& _ptr_field_tuple) {
auto field_tuple_to_named_tuple(PtrFieldTuple& _ptr_field_tuple) {
const auto ft_to_nt = []<class... Fields>(const Fields&... _fields) {
return make_named_tuple(_fields...);
};
Expand All @@ -54,19 +54,19 @@ auto field_tuple_to_named_tuple(const PtrFieldTuple& _ptr_field_tuple) {
/// Generates a named tuple that contains pointers to the original values in
/// the struct.
template <class T>
auto to_ptr_named_tuple(const T& _t) {
auto to_ptr_named_tuple(T&& _t) {
if constexpr (has_fields<std::decay_t<T>>()) {
if constexpr (std::is_pointer_v<std::decay_t<T>>) {
return to_ptr_named_tuple(*_t);
} else if constexpr (is_named_tuple_v<std::decay_t<T>>) {
return nt_to_ptr_named_tuple(_t);
} else {
const auto ptr_field_tuple = to_ptr_field_tuple(_t);
auto ptr_field_tuple = to_ptr_field_tuple(_t);
return field_tuple_to_named_tuple(ptr_field_tuple);
}
} else {
using FieldNames = rfl::field_names_t<T>;
const auto flattened_ptr_tuple = to_flattened_ptr_tuple(_t);
auto flattened_ptr_tuple = to_flattened_ptr_tuple(_t);
return copy_flattened_tuple_to_named_tuple<FieldNames>(flattened_ptr_tuple);
}
}
Expand Down
2 changes: 1 addition & 1 deletion include/rfl/internal/wrap_in_fields.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ auto wrap_in_fields(auto&& _tuple, Fields&&... _fields) {
return std::make_tuple(std::move(_fields)...);
} else {
auto value = std::move(std::get<i>(_tuple));
using Type = std::decay_t<decltype(value)>;
using Type = std::decay_t<std::remove_pointer_t<decltype(value)>>;
if constexpr (is_flatten_field_v<Type>) {
// The problem here is that the FieldNames are already flattened, but this
// is not, so we need to determine how many field names to skip.
Expand Down
19 changes: 19 additions & 0 deletions include/rfl/to_view.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef RFL_TO_VIEW_HPP_
#define RFL_TO_VIEW_HPP_

#include <iostream>
#include <tuple>
#include <type_traits>

#include "rfl/internal/to_ptr_named_tuple.hpp"

namespace rfl {

template <class T>
auto to_view(T& _t) {
return internal::to_ptr_named_tuple(_t);
}

} // namespace rfl

#endif
34 changes: 34 additions & 0 deletions tests/json/test_view.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <cassert>
#include <iostream>
#include <rfl.hpp>
#include <rfl/json.hpp>
#include <source_location>
#include <string>
#include <vector>

#include "test_replace.hpp"
#include "write_and_read.hpp"

namespace test_view {

struct Person {
std::string first_name;
std::string last_name;
int age;
};

void test() {
std::cout << std::source_location::current().function_name() << std::endl;

auto lisa = Person{.first_name = "Lisa", .last_name = "Simpson", .age = 8};

const auto view = rfl::to_view(lisa);

*view.get<0>() = "Maggie";
*std::get<1>(view.values()) = "Simpson";
*view.get<"age">() = 0;

write_and_read(lisa,
R"({"first_name":"Maggie","last_name":"Simpson","age":0})");
}
} // namespace test_view
4 changes: 4 additions & 0 deletions tests/json/test_view.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace test_view {
void test();
}

2 changes: 2 additions & 0 deletions tests/json/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
#include "test_unordered_multiset.hpp"
#include "test_unordered_set.hpp"
#include "test_variant.hpp"
#include "test_view.hpp"

int main() {
test_readme_example::test();
Expand Down Expand Up @@ -120,6 +121,7 @@ int main() {
test_as::test();
test_as2::test();
test_as_flatten::test();
test_view::test();

test_custom_constructor::test();

Expand Down

0 comments on commit 7de20db

Please sign in to comment.