Implementing the visitor pattern using C++ Templates

前端 未结 3 582
猫巷女王i
猫巷女王i 2020-12-05 00:47

I\'ve been trying to reduce the amount of boilerplate in my code, by using C++ Templates to implement the visitor pattern. So far I\'ve come up with this:

cl         


        
3条回答
  •  一整个雨季
    2020-12-05 01:40

    I was also in need of a templated Visitor pattern, and was able to create a solution that does not involve the usage of variadic types or type lists.

    // forward declarations for our Visitable interface
    class Object;
    class Visitor;
    
    // Visitable objects can accept a visitor.
    class Visitable
    {
    public:
        virtual ~Visitable() { }
        virtual void accept_visitor(Visitor& visitor) = 0;
        virtual void accept(Object& obj);
    };
    
    // A base class, to allow downcasting
    class Object
    {
    protected:
        virtual void _f() { }
    };
    
    // Our Visitor class, which will wrap our concrete visitor implementation
    class Visitor
    {
    public:
        Visitor(Object* obj);
    
        // Base class for concrete visitors
        template
        class OfType : public Object
        {
        public:
            void visit(V* visitable) {
                D* derived = static_cast(this);
    
                // "duck-typed" method; if our derived class does not have
                // this method, compilation will fail.
                derived->on_visit(visitable);
            }
        };
    
        template
        void visit(V* visitable);
    
    private:
        Object* m_obj;
    };
    
    Visitor::Visitor(Object* obj) : m_obj(obj) { }
    
    template
    void Visitor::visit(V* visitable) {
        // check if our visitor is able to visit this instance
        OfType* visitor = dynamic_cast* >(m_obj);
        if (visitor) {
            visitor->visit(visitable);
        }
    }
    
    void Visitable::accept(Object& visitor) {
        Visitor wrapped(&visitor);
        accept_visitor(wrapped);
    }
    

    After the above interfaces are defined, create specific interfaces for a visitable object's visitor, then implement them in your concrete class:

    class This;
    
    class ThisVisitor : public Visitor::OfType
    {
    public:
        virtual void on_visit(This* item) = 0;
    };
    
    class This : public Visitable
    {
    public:
        void accept_visitor(Visitor& visitor) {
            visitor.visit(this);
        }
    };
    
    class That;
    
    class ThatVisitor : public Visitor::OfType
    {
    public:
        virtual void on_visit(That* item) = 0;
    };
    
    class That : public Visitable
    {
    public:
        void accept_visitor(Visitor& visitor) {
            visitor.visit(this);
        }
    };
    
    class MyVisitor : public ThisVisitor, public ThatVisitor
    {
    public:
        void on_visit(This* item) { printf("This!"); }
        void on_visit(That* item) { printf("That!"); }
    };
    
    int main(int argc, const char* argv[] {
        This item1;
        That item2;
        MyVisitor visitor;
        item1.accept(visitor);   // "This!"
        item2.accept(visitor);   // "That!"
    }
    

    You could also skip the visitor interfaces entirely and have your concrete visitor derive from OfType directly, but I find using the former is better for extending your visitor as new classes are defined (That should not care about who visits it as long as it is of type ThatVisitor).

提交回复
热议问题