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:
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));
}
};
And each subclass of Visitable looks like this:
class Mesh : public Object, public Visitable<Mesh> {};
class Text : public Object, public Visitable<Text> {};
And finally the Visitor looks like this:
class Renderer : public Visitor<Mesh>, public Visitor<Text> {}
So far so good... now here's the problem:
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);
}
}
I need to somehow cast to Visitable so that I can call accept(), but obviously I don't know what T is. Alternatively I can't add a virtual accept() to the Visitable template, because I don't know what argument it should take.
Any C++ Templating guru's out there know how to make this work?
This can be done in C++11 using variadic templates. Continuing from Pete's answer:
// 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));
}
};
Subclasses of Visitable
:
class Mesh : public Object, public VisitableImpl<Mesh, Mesh, Text> {};
class Text : public Object, public VisitableImpl<Text, Mesh, Text> {};
A Visitor
subclass:
class Renderer : public Visitor<Mesh, Text> {};
It's not clear what the value_type
of your Scene
container is but you need to obtain a reference or pointer to Visitable<Mesh, Text>
on which to call 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);
}
}