15d7226515
Summary: Merge utils/visitor directory into single file. Rename Visitor to HierarchicalVisitor. Add regular Visitor. Split HierarchicalVisitor into LeafVisitor and CompositeVisitor. Add more documentation on visitor pattern. Make PostVisit and Visit return bool. Reviewers: florijan, mislav.bradac, buda Reviewed By: mislav.bradac Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D364
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
|