Attuazione del visitor pattern utilizzando i Modelli C++

Ho cercato di ridurre la quantità di standard nel mio codice, utilizzando dei Template C++ per implementare il visitor pattern. Finora mi è venuta in mente questa:

class BaseVisitor {
public:
    virtual ~BaseVisitor() {}
};

template<typename T>
class Visitor : public BaseVisitor {
public:
    virtual void visit(T& /* visitable */) = 0;
};

template<typename Derived>
class Visitable {
public:
    void accept(Visitor<Derived>& visitor) {
        visitor.visit(static_cast<Derived&>(*this));
    }
};

E ogni sottoclasse di Visitabili assomiglia a questo:

class Mesh : public Object, public Visitable<Mesh> {};
class Text : public Object, public Visitable<Text> {};

E, infine, il Visitatore sembra questo:

class Renderer : public Visitor<Mesh>, public Visitor<Text> {}

Finora tutto bene… ora ecco il problema:

for(Scene::iterator it = scene.begin(); it != scene.end(); ++it) {
    Object& object = static_cast<Object&>(*it);
    if(pre_visit(object)) {
        object.accept(this); ///Erm, what do I cast to??
        post_visit(object);
    }
}

Devo in qualche modo il cast Visitabile in modo che io possa chiamare accept(), ma ovviamente non so cosa T è. In alternativa, posso aggiungere un virtual accept() per la Visitabile modello, perché non so che argomento si dovrebbe prendere.

Qualsiasi C++ Template guru là fuori sa come fare questo lavoro?

InformationsquelleAutor Kazade | 2012-08-03

 

3 Replies
  1. 21

    Questo può essere fatto in C++11 utilizzo di modelli variadic. Proseguendo da Pete risposta:

    //Visitor template declaration
    template<typename... Types>
    class Visitor;
    
    //specialization for single type    
    template<typename T>
    class Visitor<T> {
    public:
        virtual void visit(T & visitable) = 0;
    };
    
    //specialization for multiple types
    template<typename T, typename... Types>
    class Visitor<T, Types...> : public Visitor<Types...> {
    public:
        //promote the function(s) from the base class
        using Visitor<Types...>::visit;
    
        virtual void visit(T & visitable) = 0;
    };
    
    template<typename... Types>
    class Visitable {
    public:
        virtual void accept(Visitor<Types...>& visitor) = 0;
    };
    
    template<typename Derived, typename... Types>
    class VisitableImpl : public Visitable<Types...> {
    public:
        virtual void accept(Visitor<Types...>& visitor) {
            visitor.visit(static_cast<Derived&>(*this));
        }
    };

    Sottoclassi di Visitable:

    class Mesh : public Object, public VisitableImpl<Mesh, Mesh, Text> {};
    class Text : public Object, public VisitableImpl<Text, Mesh, Text> {};

    Un Visitor sottoclasse:

    class Renderer : public Visitor<Mesh, Text> {};

    Non è chiaro qual è il value_type del vostro Scene contenitore, ma è necessario per ottenere un riferimento o puntatore a Visitable<Mesh, Text> su cui chiamare accept:

    for(Scene::iterator it = scene.begin(); it != scene.end(); ++it) {
        Visitable<Mesh, Text>& object = static_cast<Visitable<Mesh, Text>&>(*it);
        if(pre_visit(object)) {
            object.accept(*this);
            post_visit(object);
        }
    }
    • Ma questo funziona solo se accettate Visitor supporta tutte le classi specificate nel VisitableImpl specializzazione nell’ordine esatto, non è vero?
  2. 5

    Tuo BaseVisitor non fa nulla per voi, altro che arbitrario visitees per eliminare il visitatore. Invece, si desidera avere una classe base per il visitatore, che fornisce tutti dei diversi accept funzioni che potrebbe essere chiamato su di esso, e per la Visitable accettare questo visitatore.

    Per fare questo, si potrebbe utilizzare un nell’elenco tipo per definire i tipi, il visitatore può accettare, avere una base visitee di classe, che prende il tipo di elenco e aggiungere il tipo di elenco parametro visitee attuazione.

    sketch di esempio:

    //assuming a typelist has typedefs first and second and a 
    //type 'empty' representing end of type list
    
    template<typename Types>
    class Visitor : public Visitor<Types::second> {
    public:
        //visitor has a visit function for each type in Types
        virtual void visit(typename Types::first& visitable) = 0;
    };
    
    template<> class Visitor<empty> { };
    
    template<typename Types>
    class Visitable{
        public:
        //base accepts a visitor which can visit any type in Types
        virtual void accept(Visitor<Types>& visitor) = 0;
    };
    
    template<typename Derived, typename Types>
    class VisitableImpl : public Visitable<Types> {
    public:
        //impl calls specific visit function 
        virtual void accept(Visitor<Types>& visitor) override {
            visitor.visit(static_cast<Derived&>(*this));
        }
    };
  3. 0

    Ho anche bisogno di un basato su modelli Visitor pattern, e stato in grado di creare una soluzione che non comporta l’utilizzo di variadic o di tipi di tipo di liste.

    //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<typename D, typename V>
        class OfType : public Object
        {
        public:
            void visit(V* visitable) {
                D* derived = static_cast<D*>(this);
    
                //"duck-typed" method; if our derived class does not have
                //this method, compilation will fail.
                derived->on_visit(visitable);
            }
        };
    
        template<typename D, typename V>
        void visit(V* visitable);
    
    private:
        Object* m_obj;
    };
    
    Visitor::Visitor(Object* obj) : m_obj(obj) { }
    
    template<typename D, typename V>
    void Visitor::visit(V* visitable) {
        //check if our visitor is able to visit this instance
        OfType<D,V>* visitor = dynamic_cast<OfType<D,V>* >(m_obj);
        if (visitor) {
            visitor->visit(visitable);
        }
    }
    
    void Visitable::accept(Object& visitor) {
        Visitor wrapped(&visitor);
        accept_visitor(wrapped);
    }

    Dopo le interfacce sopra definiti, creare interfacce specifiche per un visitabile oggetto del visitatore, quindi implementare nella propria classe di calcestruzzo:

    class This;
    
    class ThisVisitor : public Visitor::OfType<ThisVisitor, This>
    {
    public:
        virtual void on_visit(This* item) = 0;
    };
    
    class This : public Visitable
    {
    public:
        void accept_visitor(Visitor& visitor) {
            visitor.visit<ThisVisitor>(this);
        }
    };
    
    class That;
    
    class ThatVisitor : public Visitor::OfType<ThatVisitor, That>
    {
    public:
        virtual void on_visit(That* item) = 0;
    };
    
    class That : public Visitable
    {
    public:
        void accept_visitor(Visitor& visitor) {
            visitor.visit<ThatVisitor>(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!"
    }

    Si potrebbe anche saltare il visitatore interfacce di tutto e avere il vostro concreto visitatore derivare da OfType<Derived, SomeClass> direttamente, ma trovo che usare il primo è migliore per estendere il visitatore come nuove classi sono definite (That deve preoccuparsi che le visite per tutto il tempo che è di tipo ThatVisitor).

Lascia un commento