Skip to content

Commit 5d879e8

Browse files
[Util] Refactor visitor header (2/X) (#146)
Refactor the macros `M_DECLARE_VISITOR` and `M_MAKE_STL_VISITABLE` to do only the minimal amount of work necessary within the macros, and implement all else in helper template classes. Also, add good doc to the macros to explain why we still need them and can't get away with *just* templates.
1 parent de4be5c commit 5d879e8

File tree

1 file changed

+54
-10
lines changed

1 file changed

+54
-10
lines changed

include/mutable/util/Visitor.hpp

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,40 @@ struct Visitor : crtp<ConcreteVisitor, Visitor, Base>
5050
virtual void operator()(base_type &obj) { obj.accept(actual()); }
5151
};
5252

53+
/**
54+
* This helper class creates a single definition of `virtual void operator()(...)` for one subtype in a class hierarchy,
55+
* and then recursively inherits from an instantiation of that same helper class for the next subtype in the hierarchy.
56+
* Eventually, this class transitively inherits from \tparam ConcreteVisitor.
57+
*/
58+
template<typename ConcreteVisitor, typename Base, typename Class, typename... Classes>
59+
struct visitor_method_helper : visitor_method_helper<ConcreteVisitor, Base, Classes...>
60+
{
61+
using super = visitor_method_helper<ConcreteVisitor, Base, Classes...>;
62+
template<typename T> using Const = typename super::template Const<T>;
63+
using super::operator();
64+
virtual void operator()(Const<Class>&) { }
65+
};
66+
/** This specialization marks the end of the class hierarchy. It inherits from \tparam ConcreteVisitor. */
67+
template<typename ConcreteVisitor, typename Base, typename Class>
68+
struct visitor_method_helper<ConcreteVisitor, Base, Class> : Visitor<ConcreteVisitor, Base>
69+
{
70+
using super = Visitor<ConcreteVisitor, Base>;
71+
template<typename T> using Const = typename super::template Const<T>;
72+
using super::operator();
73+
virtual void operator()(Const<Class>&) { }
74+
};
75+
76+
/**
77+
* A helper class to define `virtual` visit methods for all classes in \tparam Hierarchy.
78+
*/
79+
template<typename ConcreteVisitor, typename Base, typename... Hierarchy>
80+
struct VisitorImpl : visitor_method_helper<ConcreteVisitor, Base, Hierarchy...>
81+
{
82+
using super = visitor_method_helper<ConcreteVisitor, Base, Hierarchy...>;
83+
template<typename T> using Const = typename super::template Const<T>;
84+
using super::operator();
85+
};
86+
5387
/** This helper class creates a single override of `operator()` for one subtype in a class hierarchy, and then
5488
* recursively inherits from an instantiation of that same helper class for the next subtype in the hierarchy. This
5589
* enables overriding all visit methods of \tparam Visitor. */
@@ -121,22 +155,32 @@ auto visit(Callable &&callable, Base &obj, m::tag<Callable>&& = m::tag<Callable>
121155
}
122156

123157

124-
/*----- Generate a function similar to `std::visit` to easily implement a visitor for the given base class. ----------*/
158+
/**
159+
* \def M_MAKE_STL_VISITABLE(VISITOR, BASE_CLASS, CLASS_LIST)
160+
* Defines a function `visit()` to make the class hierarchy STL-style visitable with `VISITOR`.
161+
*
162+
* All this must be done with a macro, such that the definition of the visitor class and the `visit()` function reside
163+
* in the *current* namespace (as chosen by the user of this macro). This enables [*argument-dependent lookup*
164+
* (ADL)](https://en.cppreference.com/w/cpp/language/adl).
165+
*/
125166
#define M_MAKE_STL_VISITABLE(VISITOR, BASE_CLASS, CLASS_LIST) \
126167
template<typename Callable> \
127168
auto visit(Callable &&callable, BASE_CLASS &obj, m::tag<VISITOR>&& = m::tag<VISITOR>()) { \
128169
return m::visit<Callable, VISITOR, BASE_CLASS CLASS_LIST(M_COMMA_PRE)>(std::forward<Callable>(callable), obj); \
129170
}
130171

131-
/*----- Declare a visitor to visit the class hierarchy with the given base class and list of subclasses. -------------*/
132-
#define M_DECLARE_VISIT_METHOD(CLASS) virtual void operator()(Const<CLASS>&) { };
172+
/**
173+
* \def M_DECLARE_VISITOR(VISITOR_NAME, BASE_CLASS, CLASS_LIST)
174+
* Defines a visitor `VISITOR_NAME` to visit the class hierarchy rooted in `BASE_CLASS` and with subclasses
175+
* `CLASS_LIST`. Also defines a function `visit()` to make the class hierarchy STL-style visitable with `VISITOR_NAME`.
176+
*
177+
* All this must be done with a macro, such that the definition of the visitor class and the `visit()` function reside
178+
* in the *current* namespace (as chosen by the user of this macro). This enables [*argument-dependent lookup*
179+
* (ADL)](https://en.cppreference.com/w/cpp/language/adl).
180+
*/
133181
#define M_DECLARE_VISITOR(VISITOR_NAME, BASE_CLASS, CLASS_LIST) \
134-
struct M_EXPORT VISITOR_NAME : m::detail::Visitor<VISITOR_NAME, BASE_CLASS> \
135-
{ \
136-
using super = m::detail::Visitor<VISITOR_NAME, BASE_CLASS>; \
137-
template<typename T> using Const = typename super::Const<T>; \
138-
virtual ~VISITOR_NAME() {} \
139-
void operator()(BASE_CLASS &obj) { obj.accept(*this); } \
140-
CLASS_LIST(M_DECLARE_VISIT_METHOD) \
182+
struct VISITOR_NAME : m::detail::VisitorImpl<VISITOR_NAME, BASE_CLASS CLASS_LIST(M_COMMA_PRE)> { \
183+
using super = m::detail::VisitorImpl<VISITOR_NAME, BASE_CLASS CLASS_LIST(M_COMMA_PRE)>; \
184+
using super::operator(); \
141185
}; \
142186
M_MAKE_STL_VISITABLE(VISITOR_NAME, BASE_CLASS, CLASS_LIST)

0 commit comments

Comments
 (0)