Skip to content

Commit ef39b21

Browse files
Support for lvalues in rfl::visit; fixes #184 and #187 (#203)
1 parent 2880c0b commit ef39b21

File tree

5 files changed

+227
-68
lines changed

5 files changed

+227
-68
lines changed

include/rfl/Variant.hpp

Lines changed: 108 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "internal/nth_element_t.hpp"
1515
#include "internal/variant/find_max_size.hpp"
1616
#include "internal/variant/is_alternative_type.hpp"
17+
#include "internal/variant/result_t.hpp"
1718

1819
namespace rfl {
1920

@@ -24,8 +25,6 @@ class Variant {
2425

2526
static constexpr unsigned long num_bytes_ = max_size_wrapper_.size_;
2627

27-
using LargestType = typename decltype(max_size_wrapper_)::Type;
28-
2928
using DataType = std::array<unsigned char, num_bytes_>;
3029

3130
using IndexType =
@@ -35,8 +34,11 @@ class Variant {
3534

3635
static constexpr IndexType size_ = sizeof...(AlternativeTypes);
3736

37+
template <class F>
38+
using result_t = internal::variant::result_t<F, AlternativeTypes...>;
39+
3840
template <IndexType _i>
39-
struct Index {};
41+
using Index = std::integral_constant<IndexType, _i>;
4042

4143
public:
4244
Variant() {
@@ -146,14 +148,17 @@ class Variant {
146148
}
147149

148150
template <class F>
149-
auto visit(F& _f) {
150-
using FirstAlternative = internal::nth_element_t<0, AlternativeTypes...>;
151-
using ResultType = std::remove_cvref_t<
152-
std::invoke_result_t<std::remove_cvref_t<F>, FirstAlternative&>>;
151+
result_t<F> visit(F& _f) {
152+
using ResultType = result_t<F>;
153153
if constexpr (std::is_same_v<ResultType, void>) {
154154
bool visited = false;
155155
do_visit_no_result(_f, &visited,
156156
std::make_integer_sequence<IndexType, size_>());
157+
} else if constexpr (std::is_reference_v<ResultType>) {
158+
std::remove_reference_t<ResultType>* res = nullptr;
159+
do_visit_with_reference(_f, &res,
160+
std::make_integer_sequence<IndexType, size_>());
161+
return *res;
157162
} else {
158163
auto res = std::optional<ResultType>();
159164
do_visit_with_result(_f, &res,
@@ -163,14 +168,17 @@ class Variant {
163168
}
164169

165170
template <class F>
166-
auto visit(F& _f) const {
167-
using FirstAlternative = internal::nth_element_t<0, AlternativeTypes...>;
168-
using ResultType = std::remove_cvref_t<
169-
std::invoke_result_t<std::remove_cvref_t<F>, FirstAlternative&>>;
171+
result_t<F> visit(F& _f) const {
172+
using ResultType = result_t<F>;
170173
if constexpr (std::is_same_v<ResultType, void>) {
171174
bool visited = false;
172175
do_visit_no_result(_f, &visited,
173176
std::make_integer_sequence<IndexType, size_>());
177+
} else if constexpr (std::is_reference_v<ResultType>) {
178+
std::remove_reference_t<ResultType>* res = nullptr;
179+
do_visit_with_reference(_f, &res,
180+
std::make_integer_sequence<IndexType, size_>());
181+
return *res;
174182
} else {
175183
auto res = std::optional<ResultType>();
176184
do_visit_with_result(_f, &res,
@@ -180,14 +188,17 @@ class Variant {
180188
}
181189

182190
template <class F>
183-
auto visit(const F& _f) {
184-
using FirstAlternative = internal::nth_element_t<0, AlternativeTypes...>;
185-
using ResultType = std::remove_cvref_t<
186-
std::invoke_result_t<std::remove_cvref_t<F>, FirstAlternative&>>;
191+
result_t<F> visit(const F& _f) {
192+
using ResultType = std::remove_reference_t<result_t<F>>;
187193
if constexpr (std::is_same_v<ResultType, void>) {
188194
bool visited = false;
189195
do_visit_no_result(_f, &visited,
190196
std::make_integer_sequence<IndexType, size_>());
197+
} else if constexpr (std::is_reference_v<ResultType>) {
198+
std::remove_reference_t<ResultType>* res = nullptr;
199+
do_visit_with_reference(_f, &res,
200+
std::make_integer_sequence<IndexType, size_>());
201+
return *res;
191202
} else {
192203
auto res = std::optional<ResultType>();
193204
do_visit_with_result(_f, &res,
@@ -197,14 +208,17 @@ class Variant {
197208
}
198209

199210
template <class F>
200-
auto visit(const F& _f) const {
201-
using FirstAlternative = internal::nth_element_t<0, AlternativeTypes...>;
202-
using ResultType = std::remove_cvref_t<
203-
std::invoke_result_t<std::remove_cvref_t<F>, FirstAlternative&>>;
211+
result_t<F> visit(const F& _f) const {
212+
using ResultType = result_t<F>;
204213
if constexpr (std::is_same_v<ResultType, void>) {
205214
bool visited = false;
206215
do_visit_no_result(_f, &visited,
207216
std::make_integer_sequence<IndexType, size_>());
217+
} else if constexpr (std::is_reference_v<ResultType>) {
218+
std::remove_reference_t<ResultType>* res = nullptr;
219+
do_visit_with_reference(_f, &res,
220+
std::make_integer_sequence<IndexType, size_>());
221+
return *res;
208222
} else {
209223
auto res = std::optional<ResultType>();
210224
do_visit_with_result(_f, &res,
@@ -264,32 +278,6 @@ class Variant {
264278
(visit_one(_f, _visited, Index<_is>{}), ...);
265279
}
266280

267-
template <class F, class ResultType, IndexType... _is>
268-
void do_visit_with_result(F& _f, std::optional<ResultType>* _res,
269-
std::integer_sequence<IndexType, _is...>) {
270-
auto visit_one = [this]<IndexType _i>(const F& _f,
271-
std::optional<ResultType>* _res,
272-
Index<_i>) {
273-
if (!*_res && index_ == _i) {
274-
*_res = _f(get_alternative<_i>());
275-
}
276-
};
277-
(visit_one(_f, _res, Index<_is>{}), ...);
278-
}
279-
280-
template <class F, class ResultType, IndexType... _is>
281-
void do_visit_with_result(F& _f, std::optional<ResultType>* _res,
282-
std::integer_sequence<IndexType, _is...>) const {
283-
auto visit_one = [this]<IndexType _i>(const F& _f,
284-
std::optional<ResultType>* _res,
285-
Index<_i>) {
286-
if (!*_res && index_ == _i) {
287-
*_res = _f(get_alternative<_i>());
288-
}
289-
};
290-
(visit_one(_f, _res, Index<_is>{}), ...);
291-
}
292-
293281
template <class F, IndexType... _is>
294282
void do_visit_no_result(const F& _f, bool* _visited,
295283
std::integer_sequence<IndexType, _is...>) {
@@ -316,6 +304,32 @@ class Variant {
316304
(visit_one(_f, _visited, Index<_is>{}), ...);
317305
}
318306

307+
template <class F, class ResultType, IndexType... _is>
308+
void do_visit_with_result(F& _f, std::optional<ResultType>* _res,
309+
std::integer_sequence<IndexType, _is...>) {
310+
auto visit_one = [this]<IndexType _i>(const F& _f,
311+
std::optional<ResultType>* _res,
312+
Index<_i>) {
313+
if (!*_res && index_ == _i) {
314+
*_res = _f(get_alternative<_i>());
315+
}
316+
};
317+
(visit_one(_f, _res, Index<_is>{}), ...);
318+
}
319+
320+
template <class F, class ResultType, IndexType... _is>
321+
void do_visit_with_result(F& _f, std::optional<ResultType>* _res,
322+
std::integer_sequence<IndexType, _is...>) const {
323+
auto visit_one = [this]<IndexType _i>(const F& _f,
324+
std::optional<ResultType>* _res,
325+
Index<_i>) {
326+
if (!*_res && index_ == _i) {
327+
*_res = _f(get_alternative<_i>());
328+
}
329+
};
330+
(visit_one(_f, _res, Index<_is>{}), ...);
331+
}
332+
319333
template <class F, class ResultType, IndexType... _is>
320334
void do_visit_with_result(const F& _f, std::optional<ResultType>* _res,
321335
std::integer_sequence<IndexType, _is...>) {
@@ -342,6 +356,54 @@ class Variant {
342356
(visit_one(_f, _res, Index<_is>{}), ...);
343357
}
344358

359+
template <class F, class ResultType, IndexType... _is>
360+
void do_visit_with_reference(F& _f, ResultType** _res,
361+
std::integer_sequence<IndexType, _is...>) {
362+
const auto visit_one = [this]<IndexType _i>(const F& _f, ResultType** _res,
363+
Index<_i>) {
364+
if (!*_res && index_ == _i) {
365+
*_res = &_f(get_alternative<_i>());
366+
}
367+
};
368+
(visit_one(_f, _res, Index<_is>{}), ...);
369+
}
370+
371+
template <class F, class ResultType, IndexType... _is>
372+
void do_visit_with_reference(F& _f, ResultType** _res,
373+
std::integer_sequence<IndexType, _is...>) const {
374+
const auto visit_one = [this]<IndexType _i>(const F& _f, ResultType** _res,
375+
Index<_i>) {
376+
if (!*_res && index_ == _i) {
377+
*_res = &_f(get_alternative<_i>());
378+
}
379+
};
380+
(visit_one(_f, _res, Index<_is>{}), ...);
381+
}
382+
383+
template <class F, class ResultType, IndexType... _is>
384+
void do_visit_with_reference(const F& _f, ResultType** _res,
385+
std::integer_sequence<IndexType, _is...>) {
386+
const auto visit_one = [this]<IndexType _i>(const F& _f, ResultType** _res,
387+
Index<_i>) {
388+
if (!*_res && index_ == _i) {
389+
*_res = &_f(get_alternative<_i>());
390+
}
391+
};
392+
(visit_one(_f, _res, Index<_is>{}), ...);
393+
}
394+
395+
template <class F, class ResultType, IndexType... _is>
396+
void do_visit_with_reference(const F& _f, ResultType** _res,
397+
std::integer_sequence<IndexType, _is...>) const {
398+
const auto visit_one = [this]<IndexType _i>(const F& _f, ResultType** _res,
399+
Index<_i>) {
400+
if (!*_res && index_ == _i) {
401+
*_res = &_f(get_alternative<_i>());
402+
}
403+
};
404+
(visit_one(_f, _res, Index<_is>{}), ...);
405+
}
406+
345407
template <IndexType _i>
346408
auto& get_alternative() noexcept {
347409
using CurrentType = internal::nth_element_t<_i, AlternativeTypes...>;
@@ -376,7 +438,7 @@ class Variant {
376438
IndexType index_;
377439

378440
/// The underlying data, can be any of the underlying types.
379-
alignas(LargestType) DataType data_;
441+
alignas(AlternativeTypes...) DataType data_;
380442
};
381443

382444
template <class T, class... Types>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef RFL_INTERNAL_VARIANT_RESULT_T_HPP_
2+
#define RFL_INTERNAL_VARIANT_RESULT_T_HPP_
3+
4+
#include <type_traits>
5+
6+
#include "../nth_element_t.hpp"
7+
8+
namespace rfl::internal::variant {
9+
10+
template <class F, class... AlternativeTypes>
11+
using result_t = std::remove_cv_t<std::invoke_result_t<
12+
std::remove_cvref_t<F>, internal::nth_element_t<0, AlternativeTypes...>&>>;
13+
14+
} // namespace rfl::internal::variant
15+
16+
#endif

include/rfl/visit.hpp

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "internal/StringLiteral.hpp"
99
#include "internal/VisitTree.hpp"
1010
#include "internal/VisitorWrapper.hpp"
11+
#include "internal/variant/result_t.hpp"
1112

1213
namespace rfl {
1314

@@ -22,66 +23,84 @@ inline auto visit(const Visitor& _visitor, const Literal<_fields...> _literal,
2223
}
2324

2425
template <class F, class... AlternativeTypes>
25-
inline auto visit(F& _f, Variant<AlternativeTypes...>& _v) {
26+
inline internal::variant::result_t<F, AlternativeTypes...> visit(
27+
F& _f, Variant<AlternativeTypes...>& _v) {
2628
return _v.visit(_f);
2729
}
2830

2931
template <class F, class... AlternativeTypes>
30-
inline auto visit(F& _f, Variant<AlternativeTypes...>&& _v) {
32+
inline internal::variant::result_t<F, AlternativeTypes...> visit(
33+
F& _f, Variant<AlternativeTypes...>&& _v) {
3134
return _v.visit(_f);
3235
}
3336

3437
template <class F, class... AlternativeTypes>
35-
inline auto visit(F& _f, const Variant<AlternativeTypes...>& _v) {
38+
inline internal::variant::result_t<F, AlternativeTypes...> visit(
39+
F& _f, const Variant<AlternativeTypes...>& _v) {
3640
return _v.visit(_f);
3741
}
3842

3943
template <class F, class... AlternativeTypes>
40-
inline auto visit(const F& _f, Variant<AlternativeTypes...>& _v) {
44+
inline internal::variant::result_t<F, AlternativeTypes...> visit(
45+
const F& _f, Variant<AlternativeTypes...>& _v) {
4146
return _v.visit(_f);
4247
}
4348

4449
template <class F, class... AlternativeTypes>
45-
inline auto visit(const F& _f, Variant<AlternativeTypes...>&& _v) {
50+
inline internal::variant::result_t<F, AlternativeTypes...> visit(
51+
const F& _f, Variant<AlternativeTypes...>&& _v) {
4652
return _v.visit(_f);
4753
}
4854

4955
template <class F, class... AlternativeTypes>
50-
inline auto visit(const F& _f, const Variant<AlternativeTypes...>& _v) {
56+
inline internal::variant::result_t<F, AlternativeTypes...> visit(
57+
const F& _f, const Variant<AlternativeTypes...>& _v) {
5158
return _v.visit(_f);
5259
}
5360

54-
template <class F, internal::StringLiteral _discriminator, class... Args>
55-
inline auto visit(F& _f, TaggedUnion<_discriminator, Args...>& _tagged_union) {
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) {
5665
return _tagged_union.variant().visit(_f);
5766
}
5867

59-
template <class F, internal::StringLiteral _discriminator, class... Args>
60-
inline auto visit(F& _f, TaggedUnion<_discriminator, Args...>&& _tagged_union) {
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) {
6172
return _tagged_union.variant().visit(_f);
6273
}
6374

64-
template <class F, internal::StringLiteral _discriminator, class... Args>
65-
inline auto visit(F& _f,
66-
const TaggedUnion<_discriminator, Args...>& _tagged_union) {
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) {
6780
return _tagged_union.variant().visit(_f);
6881
}
6982

70-
template <class F, internal::StringLiteral _discriminator, class... Args>
71-
inline auto visit(const F& _f,
72-
TaggedUnion<_discriminator, Args...>& _tagged_union) {
83+
template <class F, internal::StringLiteral _discriminator,
84+
class... AlternativeTypes>
85+
inline internal::variant::result_t<F, AlternativeTypes...> visit(
86+
const F& _f,
87+
TaggedUnion<_discriminator, AlternativeTypes...>& _tagged_union) {
7388
return _tagged_union.variant().visit(_f);
7489
}
7590

76-
template <class F, internal::StringLiteral _discriminator, class... Args>
77-
inline auto visit(const F& _f,
78-
TaggedUnion<_discriminator, Args...>&& _tagged_union) {
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) {
7996
return _tagged_union.variant().visit(_f);
8097
}
8198

82-
template <class F, internal::StringLiteral _discriminator, class... Args>
83-
inline auto visit(const F& _f,
84-
const TaggedUnion<_discriminator, Args...>& _tagged_union) {
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) {
85104
return _tagged_union.variant().visit(_f);
86105
}
87106

0 commit comments

Comments
 (0)