1
1
#pragma once
2
2
3
3
#include < mutable/mutable-config.hpp>
4
+ #include < mutable/util/crtp.hpp>
5
+ #include < mutable/util/fn.hpp>
4
6
#include < mutable/util/macro.hpp>
5
7
#include < mutable/util/some.hpp>
6
8
#include < mutable/util/tag.hpp>
@@ -15,66 +17,119 @@ struct visit_stop_recursion { };
15
17
/* * Exception class which can be thrown to skip recursion of the subtree in pre-order visitors. */
16
18
struct visit_skip_subtree { };
17
19
18
- /* * Visitor base class. */
19
- template <typename V, typename Base>
20
- struct Visitor
20
+ /* *
21
+ * Visitor base class, using CRTP.
22
+ *
23
+ * \tparam ConcreteVisitor is the actual type of the visitor, \tparam Base is the base type of the class hierarchy to
24
+ * visit.
25
+ */
26
+ template <typename ConcreteVisitor, typename Base>
27
+ struct Visitor : crtp<ConcreteVisitor, Visitor, Base>
21
28
{
29
+ using crtp<ConcreteVisitor, Visitor, Base>::actual;
30
+
22
31
/* * Whether the visited objects are `const`-qualified. */
23
32
static constexpr bool is_const = std::is_const_v<Base>;
24
33
25
34
/* * The base class of the class hierarchy to visit. */
26
35
using base_type = Base;
27
36
28
37
/* * The concrete type of the visitor. Uses the CRTP design. */
29
- using visitor_type = V ;
38
+ using visitor_type = ConcreteVisitor ;
30
39
31
40
/* * A helper type to apply the proper `const`-qualification to parameters. */
32
41
template <typename T>
33
42
using Const = std::conditional_t <is_const, const T, T>;
34
43
35
44
/* * Visit the object `obj`. */
36
- void operator ()(base_type &obj) { static_cast <V*>(this )->operator ()(obj); }
45
+ void operator ()(base_type &obj) { actual ()(obj); }
46
+ };
47
+
48
+ /* * This helper class creates a single override of `operator()` for one subtype in a class hierarchy, and then
49
+ * recursively inherits from an instantiation of that same helper class for the next subtype in the hierarchy. This
50
+ * enables overriding all visit methods of \tparam Visitor. */
51
+ template <typename Callable, typename ResultType, typename Visitor, typename Class, typename ... Classes>
52
+ struct visit_helper : visit_helper<Callable, ResultType, Visitor, Classes...>
53
+ {
54
+ using super = visit_helper<Callable, ResultType, Visitor, Classes...>;
55
+
56
+ visit_helper (Callable &callable, std::optional<some<ResultType>> &result) : super(callable, result) { }
57
+
58
+ using super::operator ();
59
+ void operator ()(typename Visitor::template Const<Class> &obj) override {
60
+ if constexpr (std::is_same_v<void , ResultType>) { this ->callable (obj); }
61
+ else { super::result = m::some<ResultType>(this ->callable (obj)); }
62
+ }
63
+ };
64
+ /* * This specialization marks the end of the class hierarchy. It inherits from \tparam Visitor to override the visit
65
+ * methods. */
66
+ template <typename Callable, typename ResultType, typename Visitor, typename Class>
67
+ struct visit_helper <Callable, ResultType, Visitor, Class> : Visitor
68
+ {
69
+ Callable &callable;
70
+ std::optional<some<ResultType>> &result;
71
+
72
+ visit_helper (Callable &callable, std::optional<some<ResultType>> &result) : callable(callable), result(result) { }
73
+
74
+ using Visitor::operator ();
75
+ void operator ()(typename Visitor::template Const<Class> &obj) override {
76
+ if constexpr (std::is_same_v<void , ResultType>) { this ->callable (obj); }
77
+ else { result = m::some<ResultType>(this ->callable (obj)); }
78
+ }
37
79
};
38
80
81
+ /* *
82
+ * Generic implementation to visit a class hierarchy, with similar syntax as `std::visit`. This generic implementation
83
+ * is meant to be *instantiated* once for each class hierarchy that should be `m::visit()`-able. The class hierarchy
84
+ * must provide a \tparam Visitor implementing our `m::Visitor` base class.
85
+ *
86
+ * The unnamed third parameter is used for [*tag
87
+ * dispatching*](https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Tag_Dispatching) to select a particular visitor to
88
+ * apply. This is very useful if, for example, you have a compositor pattern implementing a tree structure of objects
89
+ * and you want to provide pre-order and post-order visitors. These iterators inherit from `Visitor` base and implement
90
+ * the pre- or post-order traversal. You can then call to `m::visit()` and provide `m::tag<MyPreOrderVisitor>` to
91
+ * select the desired traversal order.
92
+ *
93
+ * For an example of how to implement and use the tag dispatch, see `Expr::get_required()` and
94
+ * `ConstPreOrderExprVisitor`.
95
+ */
96
+ template <typename Callable, typename Visitor, typename Base, typename ... Hierarchy>
97
+ auto visit (Callable &&callable, Base &obj, m::tag<Callable>&& = m::tag<Callable>())
98
+ {
99
+ // /> compute the result type as the common type of the return types of all visitor applications
100
+ using result_type = std::common_type_t < std::invoke_result_t <Callable, typename Visitor::template Const<Hierarchy>&>... >;
101
+
102
+ std::optional<some<result_type>> result;
103
+
104
+ /* ----- Create a `visit_helper` and then invoke it on the `obj`. -----*/
105
+ visit_helper<Callable, result_type, Visitor, Hierarchy...> V (callable, result);
106
+ try { V (obj); } catch (visit_stop_recursion) { }
107
+ if constexpr (not std::is_same_v<void , result_type>) {
108
+ return std::move (V.result ->value );
109
+ } else {
110
+ return ; // result is `void`
111
+ }
39
112
}
40
113
41
- /* ----- Generate a function similar to `std::visit` to easily implement a visitor for the given base class. ----------*/
42
- #define M_GET_INVOKE_RESULT (CLASS ) std::invoke_result_t <Vis, Const<CLASS>&>,
43
- #define M_MAKE_STL_VISIT_METHOD (CLASS ) void operator ()(Const<CLASS> &obj) override { \
44
- if constexpr (std::is_same_v<void , result_type>) { vis (obj); } \
45
- else { result = m::some<result_type>(vis (obj)); } \
46
114
}
115
+
116
+
117
+ /* ----- Generate a function similar to `std::visit` to easily implement a visitor for the given base class. ----------*/
47
118
#define M_MAKE_STL_VISITABLE (VISITOR, BASE_CLASS, CLASS_LIST ) \
48
- template <typename Vis> \
49
- auto M_EXPORT visit (Vis &&vis, BASE_CLASS &obj, m::tag<VISITOR>&& = m::tag<VISITOR>()) { \
50
- struct V : VISITOR { \
51
- using result_type = std::common_type_t < CLASS_LIST(M_GET_INVOKE_RESULT) std::invoke_result_t <Vis, Const<M_EVAL(M_DEFER1(M_HEAD)(CLASS_LIST(M_COMMA)))>&> >; \
52
- Vis &&vis; \
53
- std::optional<m::some<result_type>> result; \
54
- V (Vis &&vis) : vis(std::forward<Vis>(vis)), result(std::nullopt) { } \
55
- V (const V&) = delete ; \
56
- V (V&&) = default ; \
57
- using VISITOR::operator (); \
58
- CLASS_LIST (M_MAKE_STL_VISIT_METHOD) \
59
- }; \
60
- V v (std::forward<Vis>(vis)); \
61
- try { v (obj); } catch (visit_stop_recursion) { } \
62
- if constexpr (not std::is_same_v<void , typename V::result_type>) { \
63
- return std::move (v.result ->value ); \
64
- } else { \
65
- return ; \
66
- } \
119
+ template <typename Callable> \
120
+ auto visit (Callable &&callable, BASE_CLASS &obj, m::tag<VISITOR>&& = m::tag<VISITOR>()) { \
121
+ return m::visit<Callable, VISITOR, BASE_CLASS CLASS_LIST (M_COMMA_PRE)>(std::forward<Callable>(callable), obj); \
67
122
}
68
123
69
124
/* ----- Declare a visitor to visit the class hierarchy with the given base class and list of subclasses. -------------*/
70
125
#define M_DECLARE_VISIT_METHOD (CLASS ) virtual void operator ()(Const<CLASS>&) { };
71
- #define M_DECLARE_VISITOR (NAME , BASE_CLASS, CLASS_LIST ) \
72
- struct M_EXPORT NAME : m::Visitor<NAME , BASE_CLASS> \
126
+ #define M_DECLARE_VISITOR (VISITOR_NAME , BASE_CLASS, CLASS_LIST ) \
127
+ struct M_EXPORT VISITOR_NAME : m::Visitor<VISITOR_NAME , BASE_CLASS> \
73
128
{ \
74
- using super = m::Visitor<NAME , BASE_CLASS>; \
129
+ using super = m::Visitor<VISITOR_NAME , BASE_CLASS>; \
75
130
template <typename T> using Const = typename super::Const<T>; \
76
- virtual ~NAME () {} \
131
+ virtual ~VISITOR_NAME () {} \
77
132
void operator ()(BASE_CLASS &obj) { obj.accept (*this ); } \
78
133
CLASS_LIST (M_DECLARE_VISIT_METHOD) \
79
134
}; \
80
- M_MAKE_STL_VISITABLE (NAME , BASE_CLASS, CLASS_LIST)
135
+ M_MAKE_STL_VISITABLE (VISITOR_NAME , BASE_CLASS, CLASS_LIST)
0 commit comments