2012-08-03 20 views
15

He estado tratando de reducir la cantidad de repetición en mi código, mediante el uso de plantillas de C++ para implementar el patrón de visitante. Hasta ahora me he llegado con esto:Implementando el patrón de visitante usando las plantillas de C++

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)); 
    } 
}; 

Y cada subclase de Visitable se ve así:

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

Y, finalmente, el visitante se ve así:

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

hasta el momento tan bueno ... ahora aquí está el 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); 
    } 
} 

Necesito de alguna manera convertir a Visitable para que pueda llamar a accept(), pero obviamente no sé qué es T. Alternativamente, no puedo agregar un accept virtual() a la plantilla Visitable, porque no sé qué argumento debería tomar.

Cualquier gurú de las plantillas de C++ sabe cómo hacer que esto funcione?

+1

Aquí es la forma en que actualmente moderna para hacerlo: [visitando-sin-itinerante] (https://backwardsincompatibilities.wordpress.com/2015/10/26/visiting-without-travelling/). – davidhigh

Respuesta

20

Esto se puede hacer en 11 utilizando plantillas variadic C++. Continuando con la respuesta de Pete:

// 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)); 
    } 
}; 

Las subclases de Visitable:

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

Un Visitor subclase:

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

No está claro cuál es el value_type de su contenedor Scene es, pero es necesario para obtener una referencia o puntero al Visitable<Mesh, Text> al cual llamar al 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); 
    } 
} 
+1

Pero esto solo funciona si el 'visitante' aceptado acepta todas las clases especificadas en la especialización 'VisitableImpl' en el mismo orden exacto, ¿no es así? – grinsekrokodil

5

Su BaseVisitor no hace nada por usted, salvo permitir que los visitantes arbitrarios eliminen al visitante. En su lugar, desea tener una clase base para el visitante que proporciona todas de las diferentes funciones accept que podrían invocarse en ella, y para el Visitable para aceptar este visitante.

Para hacer esto, puede usar un type list para definir los tipos que el visitante puede aceptar, tener una clase de visita base que tome la lista de tipos y agregar la lista de tipos como parámetro a su implementación de visitae.

esbozo de ejemplo:

// 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)); 
    } 
}; 
Cuestiones relacionadas