262 lines
9.3 KiB
C++
262 lines
9.3 KiB
C++
|
/// @file visitor.hpp
|
||
|
///
|
||
|
/// @brief This file contains the generic implementation of visitor pattern.
|
||
|
///
|
||
|
/// There are 2 approaches to the pattern:
|
||
|
///
|
||
|
/// * classic visitor pattern using @c Accept and @c Visit methods, and
|
||
|
/// * hierarchical visitor which also uses @c PreVisit and @c PostVisit
|
||
|
/// methods.
|
||
|
///
|
||
|
/// Classic Visitor
|
||
|
/// ===============
|
||
|
///
|
||
|
/// Explanation on the classic visitor pattern can be found from many
|
||
|
/// sources, but here is the link to hopefully most easily accessible
|
||
|
/// information: https://en.wikipedia.org/wiki/Visitor_pattern
|
||
|
///
|
||
|
/// The idea behind the generic implementation of classic visitor pattern is to
|
||
|
/// allow returning any type via @c Accept and @c Visit methods. Traversing the
|
||
|
/// class hierarchy is relegated to the visitor classes. Therefore, visitor
|
||
|
/// should call @c Accept on children when visiting their parents. To implement
|
||
|
/// such a visitor refer to @c Visitor and @c Visitable classes.
|
||
|
///
|
||
|
/// Hierarchical Visitor
|
||
|
/// ====================
|
||
|
///
|
||
|
/// Unlike the classic visitor, the intent of this design is to allow the
|
||
|
/// visited structure itself to control the traversal. This way the internal
|
||
|
/// children structure of classes can remain private. On the other hand,
|
||
|
/// visitors may want to differentiate visiting composite types from leaf types.
|
||
|
/// Composite types are those which contain visitable children, unlike the leaf
|
||
|
/// nodes. Differentiation is accomplished by providing @c PreVisit and @c
|
||
|
/// PostVisit methods, which should be called inside @c Accept of composite
|
||
|
/// types. Regular @c Visit is only called inside @c Accept of leaf types.
|
||
|
/// To implement such a visitor refer to @c CompositeVisitor, @c LeafVisitor and
|
||
|
/// @c Visitable classes.
|
||
|
///
|
||
|
/// Implementation of hierarchical visiting is modelled after:
|
||
|
/// http://wiki.c2.com/?HierarchicalVisitorPattern
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
namespace utils {
|
||
|
|
||
|
// Don't use anonymous namespace, because each translation unit will then get a
|
||
|
// unique type. This may cause errors if one wants to check the type.
|
||
|
namespace detail {
|
||
|
|
||
|
template <typename R, class... T>
|
||
|
class VisitorBase;
|
||
|
|
||
|
template <typename R, class Head, class... Tail>
|
||
|
class VisitorBase<R, Head, Tail...> : public VisitorBase<R, Tail...> {
|
||
|
public:
|
||
|
using typename VisitorBase<R, Tail...>::ReturnType;
|
||
|
using VisitorBase<R, Tail...>::Visit;
|
||
|
virtual ReturnType Visit(Head &) = 0;
|
||
|
};
|
||
|
|
||
|
template <typename R, class T>
|
||
|
class VisitorBase<R, T> {
|
||
|
public:
|
||
|
/// @brief ReturnType of the @c Visit method.
|
||
|
using ReturnType = R;
|
||
|
virtual ~VisitorBase() = default;
|
||
|
|
||
|
/// @brief Visit an instance of @c T.
|
||
|
virtual ReturnType Visit(T &) = 0;
|
||
|
};
|
||
|
|
||
|
|
||
|
template <class... T>
|
||
|
class CompositeVisitorBase;
|
||
|
|
||
|
template <class Head, class... Tail>
|
||
|
class CompositeVisitorBase<Head, Tail...>
|
||
|
: public CompositeVisitorBase<Tail...> {
|
||
|
public:
|
||
|
virtual bool PreVisit(Head &) { return true; }
|
||
|
virtual bool PostVisit(Head &) { return true; }
|
||
|
|
||
|
using CompositeVisitorBase<Tail...>::PreVisit;
|
||
|
using CompositeVisitorBase<Tail...>::PostVisit;
|
||
|
};
|
||
|
|
||
|
template <class T>
|
||
|
class CompositeVisitorBase<T> {
|
||
|
public:
|
||
|
/// @brief Start visiting an instance of *composite* type @c TVisitable.
|
||
|
///
|
||
|
/// This function should be used to control whether the visitor should be sent
|
||
|
/// further down the tree of classes. It is only called at the start of
|
||
|
/// @c Accept method of a composite type. The default implementation returns
|
||
|
/// true, which means that the visiting should continue.
|
||
|
///
|
||
|
/// @return bool indicating whether to continue visiting.
|
||
|
virtual bool PreVisit(T &) { return true; }
|
||
|
/// @brief Finish visiting an instance of *composite* type @c TVisitable.
|
||
|
///
|
||
|
/// This function should be used to control whether the visitor should be sent
|
||
|
/// to the siblings of currently visited instance. It is called at the end of
|
||
|
/// @c Accept method in a composite type. The default implementation returns
|
||
|
/// true, which means that visiting should continue.
|
||
|
///
|
||
|
/// @return bool indicating whether to continue visiting.
|
||
|
virtual bool PostVisit(T &) { return true; }
|
||
|
};
|
||
|
|
||
|
} // namespace detail
|
||
|
|
||
|
/// @brief Inherit from this class if you want to visit TVisitable types.
|
||
|
///
|
||
|
/// This visitor is the standard implementation of visitor pattern, where the
|
||
|
/// traversal should be done in the visitor implementation itself. This is
|
||
|
/// different from @c CompositeVisitor, where the traversal is handled by
|
||
|
/// visited classes. Therefore, this visitor contains only the @c Visit method,
|
||
|
/// which has a generic @c ReturnType.
|
||
|
///
|
||
|
/// Example usage:
|
||
|
/// @code
|
||
|
/// // Typedef for convenience or to establish a base class of visitors.
|
||
|
/// typedef Visitor<TypedValue, Identifier, AddOp> ExpressionVisitorBase;
|
||
|
/// class ExpressionVisitor : public ExpressionVisitorBase {
|
||
|
/// public:
|
||
|
/// using ExpressionVisitorBase::Visit;
|
||
|
///
|
||
|
/// TypedValue Visit(Identifier &ident) override {
|
||
|
/// // Visiting Identifier returns the value of it from execution frame.
|
||
|
/// return frame_[ident];
|
||
|
/// }
|
||
|
/// TypedValue Visit(AddOp &add_op) override {
|
||
|
/// // Visiting '+' sums the evaluation of both sides.
|
||
|
/// auto res1 = add_op.expression1_->Accept(*this);
|
||
|
/// auto res2 = add_op.expression2_->Accept(*this);
|
||
|
/// return res1 + res2;
|
||
|
/// }
|
||
|
/// };
|
||
|
/// @endcode
|
||
|
///
|
||
|
/// @sa Visitable
|
||
|
/// @sa CompositeVisitor
|
||
|
/// @sa detail::VisitorBase<R, T>::Visit
|
||
|
template <typename TReturn, class... TVisitable>
|
||
|
class Visitor : public detail::VisitorBase<TReturn, TVisitable...> {
|
||
|
public:
|
||
|
using typename detail::VisitorBase<TReturn, TVisitable...>::ReturnType;
|
||
|
using detail::VisitorBase<TReturn, TVisitable...>::Visit;
|
||
|
};
|
||
|
|
||
|
/// @brief Inherit from this class if you want to visit *leaf* TVisitable types.
|
||
|
///
|
||
|
/// This visitor is meant for hierarchical visiting of classes, where the
|
||
|
/// traversal is done by the visited classes themselves. It should be paired
|
||
|
/// with @c CompositeVisitor.
|
||
|
///
|
||
|
/// The @c Visit method should return true, if the visitor wishes to continue
|
||
|
/// traversing the sibling leaf classes.
|
||
|
template <class... TVisitable>
|
||
|
using LeafVisitor = Visitor<bool, TVisitable...>;
|
||
|
|
||
|
/// @brief Inherit from this class if you want to visit *composite* TVisitable
|
||
|
/// types.
|
||
|
///
|
||
|
/// This visitor is meant for hierarchical visiting of classes, where the
|
||
|
/// traversal is done by the visited classes themselves. Therefore, this visitor
|
||
|
/// contains @c PreVisit and @c PostVisit methods which are only called when
|
||
|
/// entering and leaving *composite* classes. It should be paired with @c
|
||
|
/// LeafVisitor. The standard @c Visit method is called only on *leaf* classes,
|
||
|
/// which do not have any visitable children in them. If you wish to use the
|
||
|
/// regular visitor pattern, refer to @c Visitor.
|
||
|
///
|
||
|
/// Example usage:
|
||
|
/// @code
|
||
|
///
|
||
|
/// class ExpressionVisitor
|
||
|
/// : public CompositeVisitor<AddOp>, // AddOp is a composite type
|
||
|
/// public LeafVisitor<Identifier> { // Identifier is a leaf type
|
||
|
/// public:
|
||
|
/// using CompositeVisitor::PreVisit;
|
||
|
/// using CompositeVisitor::PostVisit;
|
||
|
/// using LeafVisitor::Visit;
|
||
|
///
|
||
|
/// bool PreVisit(AddOp &add_op) override {
|
||
|
/// // Custom implementation for *composite* AddOp expression.
|
||
|
/// }
|
||
|
///
|
||
|
/// void Visit(Identifier &identifier) override {
|
||
|
/// // Custom implementation for *leaf* Identifier expression.
|
||
|
/// }
|
||
|
/// };
|
||
|
/// @endcode
|
||
|
///
|
||
|
/// @sa Visitable
|
||
|
/// @sa LeafVisitor
|
||
|
/// @sa Visitor
|
||
|
/// @sa detail::CompositeVisitorBase<T>::PreVisit
|
||
|
/// @sa detail::CompositeVisitorBase<T>::PostVisit
|
||
|
template <class... TVisitable>
|
||
|
class CompositeVisitor : public detail::CompositeVisitorBase<TVisitable...> {
|
||
|
public:
|
||
|
using detail::CompositeVisitorBase<TVisitable...>::PreVisit;
|
||
|
using detail::CompositeVisitorBase<TVisitable...>::PostVisit;
|
||
|
};
|
||
|
|
||
|
/// @brief Inherit from this class to allow visiting from TVisitor class.
|
||
|
///
|
||
|
/// Example usage with @c CompositeVisitor:
|
||
|
/// @code
|
||
|
/// class Expression : public Visitable<ExpressionVisitor> { ... };
|
||
|
///
|
||
|
/// class Identifier : public ExpressionVisitor {
|
||
|
/// public:
|
||
|
/// // Use default Accept implementation, since this is a *leaf* type.
|
||
|
/// DEFVISITABLE(ExpressionVisitor)
|
||
|
/// ....
|
||
|
/// };
|
||
|
///
|
||
|
/// class AddOp : public Expression {
|
||
|
/// public:
|
||
|
/// // Implement custom Accept, since this is a *composite* type.
|
||
|
/// bool Accept(ExpressionVisitor &visitor) override {
|
||
|
/// if (visitor.PreVisit(*this)) {
|
||
|
/// // Send visitor to children. Accept returns bool, which when false
|
||
|
/// // should stop the traversal to siblings.
|
||
|
/// expression1_->Accept(*this) && expression2_->Accept(*this);
|
||
|
/// }
|
||
|
/// return visitor.PostVisit(*this);
|
||
|
/// }
|
||
|
/// ...
|
||
|
///
|
||
|
/// private:
|
||
|
/// Expression *expression1_;
|
||
|
/// Expression *expression1_;
|
||
|
/// ...
|
||
|
/// };
|
||
|
/// @endcode
|
||
|
///
|
||
|
/// @sa DEFVISITABLE
|
||
|
/// @sa Visitor
|
||
|
/// @sa LeafVisitor
|
||
|
/// @sa CompositeVisitor
|
||
|
template <class TVisitor>
|
||
|
class Visitable {
|
||
|
public:
|
||
|
virtual ~Visitable() = default;
|
||
|
/// @brief Accept the @c TVisitor instance and call its @c Visit method.
|
||
|
virtual typename TVisitor::ReturnType Accept(TVisitor &) = 0;
|
||
|
|
||
|
/// Default implementation for @c utils::Visitable::Accept, which works for
|
||
|
/// visitors of @c TVisitor type. This should be used to implement regular
|
||
|
/// @c utils::Visitor, as well as for *leaf* types when accepting
|
||
|
/// @c utils::CompositeVisitor.
|
||
|
///
|
||
|
/// @sa utils::Visitable
|
||
|
#define DEFVISITABLE(TVisitor) \
|
||
|
TVisitor::ReturnType Accept(TVisitor &visitor) override { \
|
||
|
return visitor.Visit(*this); \
|
||
|
}
|
||
|
};
|
||
|
|
||
|
} // namespace utils
|