Skip to content

Commit 3fc43af

Browse files
committed
Recursive traversal of types to generate required pools
1 parent fab1e15 commit 3fc43af

File tree

5 files changed

+195
-84
lines changed

5 files changed

+195
-84
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#pragma once
2+
3+
#include <immer/flex_vector.hpp>
4+
#include <immer/map.hpp>
5+
#include <immer/set.hpp>
6+
#include <immer/table.hpp>
7+
#include <immer/vector.hpp>
8+
9+
#include <boost/hana.hpp>
10+
11+
namespace immer::persist::util {
12+
13+
namespace detail {
14+
15+
namespace hana = boost::hana;
16+
17+
template <class T, class = void>
18+
struct get_inner_types_t
19+
{
20+
static auto apply()
21+
{
22+
return hana::make_tuple(
23+
hana::make_pair(hana::type_c<T>, BOOST_HANA_STRING("")));
24+
}
25+
};
26+
27+
template <class T>
28+
struct get_inner_types_t<T, std::enable_if_t<hana::Struct<T>::value>>
29+
{
30+
static auto apply()
31+
{
32+
auto value = T{};
33+
return hana::transform(hana::keys(value), [&](auto key) {
34+
const auto& member = hana::at_key(value, key);
35+
return hana::make_pair(hana::typeid_(member), key);
36+
});
37+
}
38+
};
39+
40+
template <typename T,
41+
typename MemoryPolicy,
42+
immer::detail::rbts::bits_t B,
43+
immer::detail::rbts::bits_t BL>
44+
struct get_inner_types_t<immer::vector<T, MemoryPolicy, B, BL>>
45+
: get_inner_types_t<T>
46+
{};
47+
48+
template <typename T,
49+
typename MemoryPolicy,
50+
immer::detail::rbts::bits_t B,
51+
immer::detail::rbts::bits_t BL>
52+
struct get_inner_types_t<immer::flex_vector<T, MemoryPolicy, B, BL>>
53+
: get_inner_types_t<T>
54+
{};
55+
56+
template <typename T, typename MemoryPolicy>
57+
struct get_inner_types_t<immer::box<T, MemoryPolicy>> : get_inner_types_t<T>
58+
{};
59+
60+
template <typename T,
61+
typename Hash,
62+
typename Equal,
63+
typename MemoryPolicy,
64+
immer::detail::hamts::bits_t B>
65+
struct get_inner_types_t<immer::set<T, Hash, Equal, MemoryPolicy, B>>
66+
: get_inner_types_t<T>
67+
{};
68+
69+
template <typename K,
70+
typename T,
71+
typename Hash,
72+
typename Equal,
73+
typename MemoryPolicy,
74+
immer::detail::hamts::bits_t B>
75+
struct get_inner_types_t<immer::map<K, T, Hash, Equal, MemoryPolicy, B>>
76+
{
77+
static auto apply()
78+
{
79+
return hana::concat(get_inner_types_t<K>::apply(),
80+
get_inner_types_t<T>::apply());
81+
}
82+
};
83+
84+
template <typename T,
85+
typename KeyFn,
86+
typename Hash,
87+
typename Equal,
88+
typename MemoryPolicy,
89+
immer::detail::hamts::bits_t B>
90+
struct get_inner_types_t<immer::table<T, KeyFn, Hash, Equal, MemoryPolicy, B>>
91+
: get_inner_types_t<T>
92+
{};
93+
94+
constexpr auto insert_conditionally = [](auto map, auto pair) {
95+
namespace hana = hana;
96+
using contains_t =
97+
decltype(hana::is_just(hana::find(map, hana::first(pair))));
98+
if constexpr (contains_t::value) {
99+
const auto empty_string = BOOST_HANA_STRING("");
100+
using is_empty_t = decltype(hana::second(pair) == empty_string);
101+
if constexpr (is_empty_t::value) {
102+
// Do not replace with empty string
103+
return map;
104+
} else {
105+
return hana::insert(map, pair);
106+
}
107+
} else {
108+
return hana::insert(map, pair);
109+
}
110+
};
111+
112+
} // namespace detail
113+
114+
/**
115+
* Generate a map (type, member_name) for all members of a given type,
116+
* recursively.
117+
*/
118+
inline auto get_inner_types(const auto& type)
119+
{
120+
namespace hana = boost::hana;
121+
122+
constexpr auto get_for_one_type = [](auto type) {
123+
using T = typename decltype(type)::type;
124+
return detail::get_inner_types_t<T>::apply();
125+
};
126+
127+
constexpr auto get_for_many = [get_for_one_type](const auto& map) {
128+
auto new_pairs = hana::to_tuple(map) | [get_for_one_type](auto pair) {
129+
return get_for_one_type(hana::first(pair));
130+
};
131+
132+
return hana::fold_left(
133+
hana::to_map(new_pairs), map, detail::insert_conditionally);
134+
};
135+
136+
constexpr auto can_expand = [get_for_many](const auto& set) {
137+
return set != get_for_many(set);
138+
};
139+
140+
auto expanded = hana::while_(
141+
can_expand, hana::to_map(get_for_one_type(type)), get_for_many);
142+
143+
// Throw away types we don't know names for
144+
const auto empty_string = BOOST_HANA_STRING("");
145+
auto result =
146+
hana::filter(hana::to_tuple(expanded), [empty_string](auto pair) {
147+
return hana::second(pair) != empty_string;
148+
});
149+
return hana::to_map(result);
150+
}
151+
152+
} // namespace immer::persist::util

immer/extra/persist/json/json_with_pool_auto.hpp

+13-45
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <immer/extra/persist/champ/traits.hpp>
1010
#include <immer/extra/persist/rbts/traits.hpp>
1111

12+
#include <immer/extra/persist/common/type_traverse.hpp>
13+
1214
namespace immer::persist {
1315

1416
template <class T>
@@ -114,54 +116,20 @@ constexpr auto wrap_for_loading = exclude_internal_pool_types(
114116
make_conditional_func(is_persistable, to_persistable_loader));
115117

116118
/**
117-
* Generate a hana map of persistable members for a given type T.
118-
* Where key is a type (persistable immer container) and value is a name for
119-
* that pool (using the member name from the given struct). Example:
119+
* Generate a hana map of persistable members for the given type, recursively.
120+
* Example:
120121
* [(type_c<immer::map<K, V>>, "tracks")]
121122
*/
122-
template <class T>
123-
auto get_auto_pools_types(const T& value)
123+
auto get_pools_for_type(auto type)
124124
{
125-
namespace hana = boost::hana;
126-
static_assert(hana::Struct<T>::value,
127-
"get_auto_pools_types works only with types that "
128-
"implement hana::Struct concept");
129-
130-
constexpr auto is_persistable_key = [](auto key) {
131-
return is_persistable(hana::at_key(T{}, key));
132-
};
133-
134-
// A list of pairs like (hana::type_c<Container>, "member_name")
135-
auto pairs = hana::transform(
136-
hana::filter(hana::keys(value), is_persistable_key), [&](auto key) {
137-
const auto& member = hana::at_key(value, key);
138-
return hana::make_pair(hana::typeid_(member), key);
125+
namespace hana = boost::hana;
126+
auto all_types_map = util::get_inner_types(type);
127+
auto persistable =
128+
hana::filter(hana::to_tuple(all_types_map), [&](auto pair) {
129+
using T = typename decltype(+hana::first(pair))::type;
130+
return is_persistable(T{});
139131
});
140-
141-
return hana::unpack(pairs, hana::make_map);
142-
}
143-
144-
/**
145-
* Generate a hana map of persistable members for the given types and apply
146-
* manual overrides for names.
147-
* manual_overrides is a map of manual overrides.
148-
*/
149-
auto get_pools_for_types(auto types,
150-
auto manual_overrides = boost::hana::make_map())
151-
{
152-
namespace hana = boost::hana;
153-
// Automatically generate names and pools
154-
const auto names =
155-
hana::fold_left(hana::transform(types,
156-
[](auto t) {
157-
using T =
158-
typename decltype(t)::type;
159-
return get_auto_pools_types(T{});
160-
}),
161-
hana::make_map(),
162-
hana::union_);
163-
// Apply the overrides
164-
return hana::union_(names, manual_overrides);
132+
return hana::to_map(persistable);
165133
}
166134

167135
template <typename T,
@@ -265,7 +233,7 @@ auto get_auto_pool(const T& serializable,
265233
constexpr auto reload_pool_auto = [](auto wrap) {
266234
return [wrap](std::istream& is, auto pools, bool ignore_pool_exceptions) {
267235
using Pools = std::decay_t<decltype(pools)>;
268-
auto restore = util::istream_snapshot{is};
236+
auto restore = immer::util::istream_snapshot{is};
269237
pools.ignore_pool_exceptions = ignore_pool_exceptions;
270238
auto previous =
271239
json_immer_input_archive<cereal::JSONInputArchive, Pools>{

test/extra/persist/test_circular_dependency_conversion.cpp

+6-9
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,8 @@ TEST_CASE("Test exception while circular converting")
234234
};
235235
}();
236236

237-
const auto names = immer::persist::get_pools_for_types(
238-
hana::tuple_t<model::value_one, model::value_two, model::two_boxed>,
239-
hana::make_map());
237+
const auto names =
238+
immer::persist::get_pools_for_type(hana::type_c<model::value_one>);
240239
const auto [json_str, model_pool] =
241240
immer::persist::to_json_with_auto_pool(value, names);
242241
// REQUIRE(json_str == "");
@@ -398,9 +397,8 @@ TEST_CASE("Test circular dependency pools", "[conversion]")
398397
};
399398
}();
400399

401-
const auto names = immer::persist::get_pools_for_types(
402-
hana::tuple_t<model::value_one, model::value_two, model::two_boxed>,
403-
hana::make_map());
400+
const auto names =
401+
immer::persist::get_pools_for_type(hana::type_c<model::value_one>);
404402
const auto model_pools = immer::persist::get_auto_pool(value, names);
405403

406404
/**
@@ -508,9 +506,8 @@ TEST_CASE("Test circular dependency pools", "[conversion]")
508506
(void) format_load_pools;
509507
// show_type<decltype(format_load_pools)> qwe;
510508

511-
const auto format_names = immer::persist::get_pools_for_types(
512-
hana::tuple_t<format::value_one, format::value_two, format::two_boxed>,
513-
hana::make_map());
509+
const auto format_names =
510+
immer::persist::get_pools_for_type(hana::type_c<format::value_one>);
514511

515512
SECTION("vector")
516513
{

test/extra/persist/test_conversion.cpp

+8-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#include <immer/extra/persist/json/json_with_pool_auto.hpp>
44

5+
#include <immer/extra/persist/common/type_traverse.hpp>
6+
57
#include "utils.hpp"
68

79
#define DEFINE_OPERATIONS(name) \
@@ -141,10 +143,10 @@ class Z;
141143
TEST_CASE("Convert between two hierarchies via JSON compatibility",
142144
"[conversion]")
143145
{
144-
const auto model_names = immer::persist::get_pools_for_types(
145-
hana::tuple_t<model::history, model::arrangement>, hana::make_map());
146-
const auto format_names = immer::persist::get_pools_for_types(
147-
hana::tuple_t<format::history, format::arrangement>, hana::make_map());
146+
const auto model_names =
147+
immer::persist::get_pools_for_type(hana::type_c<model::history>);
148+
const auto format_names =
149+
immer::persist::get_pools_for_type(hana::type_c<format::history>);
148150
(void) format_names;
149151

150152
const auto value = model::make_example_history();
@@ -202,8 +204,8 @@ struct two_vectors
202204

203205
TEST_CASE("Not every type is converted", "[conversion]")
204206
{
205-
const auto names = immer::persist::get_pools_for_types(
206-
hana::tuple_t<two_vectors>, hana::make_map());
207+
const auto names =
208+
immer::persist::get_pools_for_type(hana::type_c<two_vectors>);
207209
const auto [json_str, pools] =
208210
immer::persist::to_json_with_auto_pool(two_vectors{}, names);
209211

0 commit comments

Comments
 (0)