/// @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 class VisitorBase; template class VisitorBase : public VisitorBase { public: using typename VisitorBase::ReturnType; using VisitorBase::Visit; virtual ReturnType Visit(Head &) = 0; }; template class VisitorBase { 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 CompositeVisitorBase; template class CompositeVisitorBase : public CompositeVisitorBase { public: virtual bool PreVisit(Head &) { return true; } virtual bool PostVisit(Head &) { return true; } using CompositeVisitorBase::PreVisit; using CompositeVisitorBase::PostVisit; }; template class CompositeVisitorBase { 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 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::Visit template class Visitor : public detail::VisitorBase { public: using typename detail::VisitorBase::ReturnType; using detail::VisitorBase::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 using LeafVisitor = Visitor; /// @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 is a composite type /// public LeafVisitor { // 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::PreVisit /// @sa detail::CompositeVisitorBase::PostVisit template class CompositeVisitor : public detail::CompositeVisitorBase { public: using detail::CompositeVisitorBase::PreVisit; using detail::CompositeVisitorBase::PostVisit; }; /// @brief Inherit from this class to allow visiting from TVisitor class. /// /// Example usage with @c CompositeVisitor: /// @code /// class Expression : public Visitable { ... }; /// /// 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 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