Реализация шаблона посетителя с использованием шаблонов С++
Я пытаюсь уменьшить количество шаблонов в моем коде, используя шаблоны С++ для реализации шаблона посетителя. До сих пор я придумал это:
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));
}
};
И каждый подкласс Visitable выглядит следующим образом:
class Mesh : public Object, public Visitable<Mesh> {};
class Text : public Object, public Visitable<Text> {};
И, наконец, посетитель выглядит так:
class Renderer : public Visitor<Mesh>, public Visitor<Text> {}
До сих пор так хорошо... теперь вот проблема:
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);
}
}
Мне нужно каким-то образом придать Visitable, чтобы я мог вызвать accept(), но, очевидно, я не знаю, что такое T. В качестве альтернативы я не могу добавить виртуальный accept() к Visitable-шаблону, потому что я не знаю, какой аргумент он должен принять.
Любой С++ Templating guru там знает, как сделать эту работу?
Ответы
Ответ 1
Это можно сделать в С++ 11 с использованием вариативных шаблонов. Продолжая от Пита ответ:
// 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));
}
};
Подклассы Visitable
:
class Mesh : public Object, public VisitableImpl<Mesh, Mesh, Text> {};
class Text : public Object, public VisitableImpl<Text, Mesh, Text> {};
Подкласс A Visitor
:
class Renderer : public Visitor<Mesh, Text> {};
Непонятно, что такое value_type
вашего контейнера Scene
, но вам нужно получить ссылку или указатель на Visitable<Mesh, Text>
, на который вызывать 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);
}
}
Ответ 2
Ваш BaseVisitor ничего не делает для вас, кроме как разрешать произвольным посетителям удалять посетителя. Вместо этого вы хотите иметь базовый класс для посетителя, который предоставляет все различные функции accept
, которые могут быть вызваны на нем, а для Visitable
- для этого посетителя.
Чтобы сделать это, вы можете использовать список типов , чтобы определить типы, которые может принять посетитель, иметь базовый класс visitee, который принимает тип список и добавьте список типов в качестве параметра в вашу реализацию visitee.
эскиз примера:
// 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));
}
};