Skip to content

Commit 7de20db

Browse files
Added support for rfl::to_view; #32
1 parent 8ed390c commit 7de20db

12 files changed

Lines changed: 123 additions & 15 deletions

File tree

README.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -238,15 +238,30 @@ struct Person {
238238
std::vector<Person> children;
239239
};
240240

241-
const auto fields = rfl::fields<Person>();
242-
243-
std::cout << "Fields in " << rfl::type_name_t<Person>().str() << ":"
244-
<< std::endl;
245-
for (const auto& f : fields) {
241+
for (const auto& f : rfl::fields<Person>()) {
246242
std::cout << "name: " << f.name() << ", type: " << f.type() << std::endl;
247243
}
248244
```
249245
246+
You can also create a view and then access these fields using `std::get` or `rfl::get`:
247+
248+
```cpp
249+
auto lisa = Person{.first_name = "Lisa", .last_name = "Simpson", .age = 8};
250+
251+
const auto view = rfl::to_view(lisa);
252+
253+
// view.values() is a std::tuple containing
254+
// pointers to the original fields.
255+
// This will modify the struct `lisa`:
256+
*std::get<0>(view.values()) = "Maggie";
257+
258+
// All of this is supported as well:
259+
*view.get<1>() = "Simpson";
260+
*view.get<"age">() = 0;
261+
*rfl::get<0>(view) = "Maggie";
262+
*rfl::get<"first_name">(view) = "Maggie";
263+
```
264+
250265
It also possible to replace fields:
251266

252267
```cpp

docs/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050

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

53+
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.
54+
5355
## 5) Supported formats
5456

5557
5.1) [JSON](https://github.com/getml/reflect-cpp/blob/main/docs/json.md)

docs/to_view.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# `rfl::to_view`
2+
3+
`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.
4+
5+
Under-the-hood, a view is a `rfl::NamedTuple` containing pointers to the original fields.
6+
7+
For example:
8+
9+
```cpp
10+
auto lisa = Person{.first_name = "Lisa", .last_name = "Simpson", .age = 8};
11+
12+
const auto view = rfl::to_view(lisa);
13+
14+
// Assigns the first field, thus modifying the struct 'lisa'.
15+
*view.get<0>() = "Maggie";
16+
17+
// view.values() is a std::tuple containing
18+
// pointers to the original fields.
19+
*std::get<1>(view.values()) = "Simpson";
20+
21+
// You can also access fields by their name.
22+
// The correctness will be ensured at compile time.
23+
*view.get<"age">() = 0;
24+
25+
// You can also access fields like this.
26+
*rfl::get<0>(view) = "Maggie";
27+
28+
// Or like this.
29+
*rfl::get<"first_name">(view) = "Maggie";
30+
```

include/rfl.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "rfl/remove_fields.hpp"
4747
#include "rfl/replace.hpp"
4848
#include "rfl/to_named_tuple.hpp"
49+
#include "rfl/to_view.hpp"
4950
#include "rfl/type_name_t.hpp"
5051
#include "rfl/visit.hpp"
5152

include/rfl/internal/bind_to_tuple.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ template <class T>
1818
constexpr auto tuple_view(T&);
1919

2020
template <class T, typename F>
21-
constexpr auto bind_to_tuple(T& _t, F&& f) {
21+
constexpr auto bind_to_tuple(T& _t, const F& _f) {
2222
auto view = tuple_view(_t);
2323
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
24-
return std::make_tuple(std::forward<F>(f)(std::get<Is>(view))...);
25-
}(std::make_index_sequence<std::tuple_size_v<decltype(view)>>());
24+
return std::make_tuple(_f(std::get<Is>(view))...);
25+
}
26+
(std::make_index_sequence<std::tuple_size_v<decltype(view)>>());
2627
}
2728

2829
/*The following boilerplate code was generated using a Python script:

include/rfl/internal/to_flattened_ptr_tuple.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ auto flatten_ptr_tuple(PtrTuple&& _t, Args... _args) {
3232
}
3333

3434
template <class T>
35-
auto to_flattened_ptr_tuple(const T& _t) {
35+
auto to_flattened_ptr_tuple(T&& _t) {
3636
return flatten_ptr_tuple(to_ptr_tuple(_t));
3737
}
3838

include/rfl/internal/to_ptr_named_tuple.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace rfl {
1919
namespace internal {
2020

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

4040
template <class PtrFieldTuple>
41-
auto field_tuple_to_named_tuple(const PtrFieldTuple& _ptr_field_tuple) {
41+
auto field_tuple_to_named_tuple(PtrFieldTuple& _ptr_field_tuple) {
4242
const auto ft_to_nt = []<class... Fields>(const Fields&... _fields) {
4343
return make_named_tuple(_fields...);
4444
};
@@ -54,19 +54,19 @@ auto field_tuple_to_named_tuple(const PtrFieldTuple& _ptr_field_tuple) {
5454
/// Generates a named tuple that contains pointers to the original values in
5555
/// the struct.
5656
template <class T>
57-
auto to_ptr_named_tuple(const T& _t) {
57+
auto to_ptr_named_tuple(T&& _t) {
5858
if constexpr (has_fields<std::decay_t<T>>()) {
5959
if constexpr (std::is_pointer_v<std::decay_t<T>>) {
6060
return to_ptr_named_tuple(*_t);
6161
} else if constexpr (is_named_tuple_v<std::decay_t<T>>) {
6262
return nt_to_ptr_named_tuple(_t);
6363
} else {
64-
const auto ptr_field_tuple = to_ptr_field_tuple(_t);
64+
auto ptr_field_tuple = to_ptr_field_tuple(_t);
6565
return field_tuple_to_named_tuple(ptr_field_tuple);
6666
}
6767
} else {
6868
using FieldNames = rfl::field_names_t<T>;
69-
const auto flattened_ptr_tuple = to_flattened_ptr_tuple(_t);
69+
auto flattened_ptr_tuple = to_flattened_ptr_tuple(_t);
7070
return copy_flattened_tuple_to_named_tuple<FieldNames>(flattened_ptr_tuple);
7171
}
7272
}

include/rfl/internal/wrap_in_fields.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ auto wrap_in_fields(auto&& _tuple, Fields&&... _fields) {
1919
return std::make_tuple(std::move(_fields)...);
2020
} else {
2121
auto value = std::move(std::get<i>(_tuple));
22-
using Type = std::decay_t<decltype(value)>;
22+
using Type = std::decay_t<std::remove_pointer_t<decltype(value)>>;
2323
if constexpr (is_flatten_field_v<Type>) {
2424
// The problem here is that the FieldNames are already flattened, but this
2525
// is not, so we need to determine how many field names to skip.

include/rfl/to_view.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#ifndef RFL_TO_VIEW_HPP_
2+
#define RFL_TO_VIEW_HPP_
3+
4+
#include <iostream>
5+
#include <tuple>
6+
#include <type_traits>
7+
8+
#include "rfl/internal/to_ptr_named_tuple.hpp"
9+
10+
namespace rfl {
11+
12+
template <class T>
13+
auto to_view(T& _t) {
14+
return internal::to_ptr_named_tuple(_t);
15+
}
16+
17+
} // namespace rfl
18+
19+
#endif

tests/json/test_view.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include <cassert>
2+
#include <iostream>
3+
#include <rfl.hpp>
4+
#include <rfl/json.hpp>
5+
#include <source_location>
6+
#include <string>
7+
#include <vector>
8+
9+
#include "test_replace.hpp"
10+
#include "write_and_read.hpp"
11+
12+
namespace test_view {
13+
14+
struct Person {
15+
std::string first_name;
16+
std::string last_name;
17+
int age;
18+
};
19+
20+
void test() {
21+
std::cout << std::source_location::current().function_name() << std::endl;
22+
23+
auto lisa = Person{.first_name = "Lisa", .last_name = "Simpson", .age = 8};
24+
25+
const auto view = rfl::to_view(lisa);
26+
27+
*view.get<0>() = "Maggie";
28+
*std::get<1>(view.values()) = "Simpson";
29+
*view.get<"age">() = 0;
30+
31+
write_and_read(lisa,
32+
R"({"first_name":"Maggie","last_name":"Simpson","age":0})");
33+
}
34+
} // namespace test_view

0 commit comments

Comments
 (0)