Skip to content

Commit 716de7b

Browse files
authored
Emplace into optional to be able to visit variant containers containi… (#234)
1 parent 88cf229 commit 716de7b

File tree

5 files changed

+64
-149
lines changed

5 files changed

+64
-149
lines changed

include/rfl/TaggedUnion.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ struct TaggedUnion {
113113
VariantType variant_;
114114
};
115115

116+
template <typename T>
117+
concept TaggedUnionBased = requires(T t) {
118+
[]<internal::StringLiteral _discriminator, typename... Args>(
119+
TaggedUnion<_discriminator, Args...> const&) {}(t);
120+
};
121+
116122
template <class T>
117123
struct PossibleTags;
118124

include/rfl/Variant.hpp

Lines changed: 17 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
#define RFL_VARIANT_HPP_
33

44
#include <array>
5-
#include <bit>
65
#include <cstdint>
76
#include <limits>
87
#include <optional>
@@ -14,7 +13,6 @@
1413
#include "internal/nth_element_t.hpp"
1514
#include "internal/variant/find_max_size.hpp"
1615
#include "internal/variant/is_alternative_type.hpp"
17-
#include "internal/variant/is_convertible_to.hpp"
1816
#include "internal/variant/result_t.hpp"
1917

2018
namespace rfl {
@@ -152,80 +150,40 @@ class Variant {
152150
}
153151

154152
template <class F>
155-
result_t<F> visit(F& _f) {
153+
result_t<F> visit(F&& _f) {
156154
using ResultType = result_t<F>;
157155
if constexpr (std::is_same_v<ResultType, void>) {
158156
bool visited = false;
159-
do_visit_no_result(_f, &visited,
157+
do_visit_no_result(std::forward<F>(_f), &visited,
160158
std::make_integer_sequence<IndexType, size_>());
161159
} else if constexpr (std::is_reference_v<ResultType>) {
162160
std::remove_reference_t<ResultType>* res = nullptr;
163-
do_visit_with_reference(_f, &res,
161+
do_visit_with_reference(std::forward<F>(_f), &res,
164162
std::make_integer_sequence<IndexType, size_>());
165163
return *res;
166164
} else {
167165
auto res = std::optional<ResultType>();
168-
do_visit_with_result(_f, &res,
166+
do_visit_with_result(std::forward<F>(_f), &res,
169167
std::make_integer_sequence<IndexType, size_>());
170168
return std::move(*res);
171169
}
172170
}
173171

174172
template <class F>
175-
result_t<F> visit(F& _f) const {
173+
result_t<F> visit(F&& _f) const {
176174
using ResultType = result_t<F>;
177175
if constexpr (std::is_same_v<ResultType, void>) {
178176
bool visited = false;
179-
do_visit_no_result(_f, &visited,
177+
do_visit_no_result(std::forward<F>(_f), &visited,
180178
std::make_integer_sequence<IndexType, size_>());
181179
} else if constexpr (std::is_reference_v<ResultType>) {
182180
std::remove_reference_t<ResultType>* res = nullptr;
183-
do_visit_with_reference(_f, &res,
181+
do_visit_with_reference(std::forward<F>(_f), &res,
184182
std::make_integer_sequence<IndexType, size_>());
185183
return *res;
186184
} else {
187185
auto res = std::optional<ResultType>();
188-
do_visit_with_result(_f, &res,
189-
std::make_integer_sequence<IndexType, size_>());
190-
return std::move(*res);
191-
}
192-
}
193-
194-
template <class F>
195-
result_t<F> visit(const F& _f) {
196-
using ResultType = std::remove_reference_t<result_t<F>>;
197-
if constexpr (std::is_same_v<ResultType, void>) {
198-
bool visited = false;
199-
do_visit_no_result(_f, &visited,
200-
std::make_integer_sequence<IndexType, size_>());
201-
} else if constexpr (std::is_reference_v<ResultType>) {
202-
std::remove_reference_t<ResultType>* res = nullptr;
203-
do_visit_with_reference(_f, &res,
204-
std::make_integer_sequence<IndexType, size_>());
205-
return *res;
206-
} else {
207-
auto res = std::optional<ResultType>();
208-
do_visit_with_result(_f, &res,
209-
std::make_integer_sequence<IndexType, size_>());
210-
return std::move(*res);
211-
}
212-
}
213-
214-
template <class F>
215-
result_t<F> visit(const F& _f) const {
216-
using ResultType = result_t<F>;
217-
if constexpr (std::is_same_v<ResultType, void>) {
218-
bool visited = false;
219-
do_visit_no_result(_f, &visited,
220-
std::make_integer_sequence<IndexType, size_>());
221-
} else if constexpr (std::is_reference_v<ResultType>) {
222-
std::remove_reference_t<ResultType>* res = nullptr;
223-
do_visit_with_reference(_f, &res,
224-
std::make_integer_sequence<IndexType, size_>());
225-
return *res;
226-
} else {
227-
auto res = std::optional<ResultType>();
228-
do_visit_with_result(_f, &res,
186+
do_visit_with_result(std::forward<F>(_f), &res,
229187
std::make_integer_sequence<IndexType, size_>());
230188
return std::move(*res);
231189
}
@@ -330,7 +288,7 @@ class Variant {
330288
std::optional<ResultType>* _res,
331289
Index<_i>) {
332290
if (!*_res && index_ == _i) {
333-
*_res = _f(get_alternative<_i>());
291+
_res->emplace(_f(get_alternative<_i>()));
334292
}
335293
};
336294
(visit_one(_f, _res, Index<_is>{}), ...);
@@ -343,7 +301,7 @@ class Variant {
343301
std::optional<ResultType>* _res,
344302
Index<_i>) {
345303
if (!*_res && index_ == _i) {
346-
*_res = _f(get_alternative<_i>());
304+
_res->emplace(_f(get_alternative<_i>()));
347305
}
348306
};
349307
(visit_one(_f, _res, Index<_is>{}), ...);
@@ -356,7 +314,7 @@ class Variant {
356314
std::optional<ResultType>* _res,
357315
Index<_i>) {
358316
if (!*_res && index_ == _i) {
359-
*_res = _f(get_alternative<_i>());
317+
_res->emplace(_f(get_alternative<_i>()));
360318
}
361319
};
362320
(visit_one(_f, _res, Index<_is>{}), ...);
@@ -369,7 +327,7 @@ class Variant {
369327
std::optional<ResultType>* _res,
370328
Index<_i>) {
371329
if (!*_res && index_ == _i) {
372-
*_res = _f(get_alternative<_i>());
330+
_res->emplace(_f(get_alternative<_i>()));
373331
}
374332
};
375333
(visit_one(_f, _res, Index<_is>{}), ...);
@@ -460,6 +418,11 @@ class Variant {
460418
alignas(AlternativeTypes...) DataType data_;
461419
};
462420

421+
template <typename V>
422+
concept VariantBased = requires(std::decay_t<V> v) {
423+
[]<typename... Args>(Variant<Args...> const&) {}(v);
424+
};
425+
463426
template <class T, class... Types>
464427
constexpr T* get_if(Variant<Types...>* _v) noexcept {
465428
const auto get = [](auto& _v) -> T* {

include/rfl/parsing/call_destructors_where_necessary.hpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ void call_destructors_where_necessary(const std::array<bool, _size>& _set,
4949
[&]<int... is>(std::integer_sequence<int, is...>) {
5050
(call_destructor_on_one_if_necessary<ViewType, _size, is>(_set, _view),
5151
...);
52-
}
53-
(std::make_integer_sequence<int, _size>());
52+
}(std::make_integer_sequence<int, _size>());
5453
}
5554

5655
} // namespace rfl::parsing

include/rfl/visit.hpp

Lines changed: 16 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
#ifndef RFL_VISIT_HPP_
22
#define RFL_VISIT_HPP_
33

4-
#include <variant>
4+
#include <utility>
55

66
#include "Literal.hpp"
77
#include "TaggedUnion.hpp"
88
#include "internal/StringLiteral.hpp"
99
#include "internal/VisitTree.hpp"
1010
#include "internal/VisitorWrapper.hpp"
11-
#include "internal/variant/result_t.hpp"
1211

1312
namespace rfl {
1413

@@ -22,104 +21,29 @@ inline auto visit(const Visitor& _visitor, const Literal<_fields...> _literal,
2221
wrapper, _literal.value(), _args...);
2322
}
2423

25-
template <class F, class... AlternativeTypes>
26-
inline internal::variant::result_t<F, AlternativeTypes...> visit(
27-
F& _f, Variant<AlternativeTypes...>& _v) {
28-
return _v.visit(_f);
24+
template <class F, VariantBased V>
25+
inline auto visit(F&& _f, V&& _v)
26+
-> decltype(std::declval<V&&>().visit(std::declval<F&&>())) {
27+
return std::forward<V>(_v).visit(std::forward<F>(_f));
2928
}
3029

31-
template <class F, class... AlternativeTypes>
32-
inline internal::variant::result_t<F, AlternativeTypes...> visit(
33-
F& _f, Variant<AlternativeTypes...>&& _v) {
34-
return _v.visit(_f);
35-
}
36-
37-
template <class F, class... AlternativeTypes>
38-
inline internal::variant::result_t<F, AlternativeTypes...> visit(
39-
F& _f, const Variant<AlternativeTypes...>& _v) {
40-
return _v.visit(_f);
41-
}
42-
43-
template <class F, class... AlternativeTypes>
44-
inline internal::variant::result_t<F, AlternativeTypes...> visit(
45-
const F& _f, Variant<AlternativeTypes...>& _v) {
46-
return _v.visit(_f);
47-
}
48-
49-
template <class F, class... AlternativeTypes>
50-
inline internal::variant::result_t<F, AlternativeTypes...> visit(
51-
const F& _f, Variant<AlternativeTypes...>&& _v) {
52-
return _v.visit(_f);
53-
}
54-
55-
template <class F, class... AlternativeTypes>
56-
inline internal::variant::result_t<F, AlternativeTypes...> visit(
57-
const F& _f, const Variant<AlternativeTypes...>& _v) {
58-
return _v.visit(_f);
59-
}
60-
61-
template <class F, internal::StringLiteral _discriminator,
62-
class... AlternativeTypes>
63-
inline internal::variant::result_t<F, AlternativeTypes...> visit(
64-
F& _f, TaggedUnion<_discriminator, AlternativeTypes...>& _tagged_union) {
65-
return _tagged_union.variant().visit(_f);
66-
}
67-
68-
template <class F, internal::StringLiteral _discriminator,
69-
class... AlternativeTypes>
70-
inline internal::variant::result_t<F, AlternativeTypes...> visit(
71-
F& _f, TaggedUnion<_discriminator, AlternativeTypes...>&& _tagged_union) {
72-
return _tagged_union.variant().visit(_f);
73-
}
74-
75-
template <class F, internal::StringLiteral _discriminator,
76-
class... AlternativeTypes>
77-
inline internal::variant::result_t<F, AlternativeTypes...> visit(
78-
F& _f,
79-
const TaggedUnion<_discriminator, AlternativeTypes...>& _tagged_union) {
80-
return _tagged_union.variant().visit(_f);
81-
}
82-
83-
template <class F, internal::StringLiteral _discriminator,
30+
template <class F, TaggedUnionBased T, internal::StringLiteral _discriminator,
8431
class... AlternativeTypes>
85-
inline internal::variant::result_t<F, AlternativeTypes...> visit(
86-
const F& _f,
87-
TaggedUnion<_discriminator, AlternativeTypes...>& _tagged_union) {
88-
return _tagged_union.variant().visit(_f);
89-
}
90-
91-
template <class F, internal::StringLiteral _discriminator,
92-
class... AlternativeTypes>
93-
inline internal::variant::result_t<F, AlternativeTypes...> visit(
94-
const F& _f,
95-
TaggedUnion<_discriminator, AlternativeTypes...>&& _tagged_union) {
96-
return _tagged_union.variant().visit(_f);
97-
}
98-
99-
template <class F, internal::StringLiteral _discriminator,
100-
class... AlternativeTypes>
101-
inline internal::variant::result_t<F, AlternativeTypes...> visit(
102-
const F& _f,
103-
const TaggedUnion<_discriminator, AlternativeTypes...>& _tagged_union) {
104-
return _tagged_union.variant().visit(_f);
105-
}
106-
107-
template <class F, class Head, class... Tail>
108-
inline auto visit(F& _f, Head& _head, Tail&... _tail) {
109-
const auto f_outer = [&](auto& _h) {
110-
const auto f_inner = [&](auto&... _t) { return _f(_h, _t...); };
111-
return visit(f_inner, _tail...);
112-
};
113-
return _head.visit(f_outer);
32+
inline auto visit(
33+
F&& _f, TaggedUnion<_discriminator, AlternativeTypes...>&& _tagged_union)
34+
-> decltype(std::declval<T&&>().variant().visit(std::declval<F&&>())) {
35+
return std::forward<T>(_tagged_union).variant().visit(std::forward<F>(_f));
11436
}
11537

11638
template <class F, class Head, class... Tail>
117-
inline auto visit(const F& _f, Head& _head, Tail&... _tail) {
39+
inline auto visit(F&& _f, Head&& _head, Tail&&... _tail) {
11840
const auto f_outer = [&](auto& _h) {
119-
const auto f_inner = [&](auto&... _t) { return _f(_h, _t...); };
120-
return visit(f_inner, _tail...);
41+
const auto f_inner = [&](auto&... _t) {
42+
return std::forward<F>(_f)(_h, _t...);
43+
};
44+
return visit(f_inner, std::forward<Tail>(_tail)...);
12145
};
122-
return _head.visit(f_outer);
46+
return std::forward<Head>(_head).visit(f_outer);
12347
}
12448

12549
} // namespace rfl

tests/json/test_rfl_variant_visit_move_only.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include <cassert>
2-
#include <iostream>
32
#include <memory>
43
#include <rfl.hpp>
54
#include <rfl/json.hpp>
@@ -19,6 +18,10 @@ struct Square {
1918
std::unique_ptr<double> width;
2019
};
2120

21+
struct Circle {
22+
double const radius;
23+
};
24+
2225
using Shapes = rfl::Variant<Rectangle, Square>;
2326

2427
TEST(json, test_rfl_variant_visit_move_only) {
@@ -29,4 +32,24 @@ TEST(json, test_rfl_variant_visit_move_only) {
2932
};
3033
EXPECT_EQ(*rfl::visit(get_width, r), 5.0);
3134
}
35+
36+
TEST(json, test_rfl_variant_visit_return_move_only_const) {
37+
auto const circle = Circle{.radius = 10};
38+
auto const variant = rfl::Variant<Circle>{circle};
39+
const auto get_radius = [](const auto& object) -> const double& {
40+
return object.radius;
41+
};
42+
auto const result = rfl::visit(get_radius, variant);
43+
EXPECT_EQ(result, circle.radius);
44+
}
45+
46+
TEST(json, test_rfl_variant_visit_return_move_only) {
47+
auto circle = Circle{.radius = 10};
48+
auto variant = rfl::Variant<Circle>{circle};
49+
auto const get_radius = [](auto& object) -> double const& {
50+
return object.radius;
51+
};
52+
auto result = rfl::visit(get_radius, variant);
53+
EXPECT_EQ(result, circle.radius);
54+
}
3255
} // namespace test_rfl_variant_visit_move_only

0 commit comments

Comments
 (0)