2012-08-03 17 views
15

Próbowałem zmniejszyć ilość tablicy skrótów w moim kodzie, za pomocą C++ Templates do wdrożenia wzorca gościa. Do tej pory mam wymyślić to:Wdrażanie wzorca gościa przy użyciu szablonów 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)); 
    } 
}; 

I każda podklasa visitable wygląda następująco:

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

I wreszcie Visitor wygląda następująco:

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

tak daleko tak dobrze ... teraz tu jest 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); 
    } 
} 

Muszę jakoś rzucić na Visitable, abym mógł zadzwonić accept(), ale oczywiście nie wiem, co to jest T. Alternatywnie nie mogę dodać wirtualnego accept() do szablonu Visitable, ponieważ nie wiem, jaki argument powinien on przyjąć.

Każdy guru Templating C++ tam wie, jak to zrobić?

+1

Oto obecnie nowoczesny sposób to zrobić: [odwiedzenie-bez-podróżowaniem] (https://backwardsincompatibilities.wordpress.com/2015/10/26/visiting-without-travelling/). – davidhigh

Odpowiedz

20

Można to zrobić w C++ 11, wykorzystując szablony o zmiennej liczbie argumentów. Kontynuując od Pete'a odpowiedź:

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

podklasy Visitable:

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

Visitor podklasy:

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

Nie jest jasne, co value_type swojego pojemnika Scene jest ale trzeba uzyskać odniesienie lub wskaźnik do Visitable<Mesh, Text>, na który należy zadzwonić pod numer 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

Ale działa to tylko wtedy, gdy zaakceptowany "użytkownik" obsługuje wszystkie klasy określone w specjalizacji 'VisitableImpl' w dokładnie tej samej kolejności, prawda? – grinsekrokodil

5

Twój BaseVisitor nic dla ciebie nie robi, poza zezwoleniem samowolnym odwiedzającym na usunięcie użytkownika. Zamiast tego chcesz mieć klasę podstawową dla odwiedzającego, która zapewnia wszystkie różnych funkcji accept, które mogą być wywoływane na nim, a dla Visitable, aby zaakceptować tego użytkownika.

Aby to zrobić, można użyć parametru type list, aby zdefiniować typy, które może zaakceptować użytkownik, posiadać klasę visitee, która przyjmuje listę typów i dodać listę typów jako parametr do implementacji visitee.

szkic przykład:

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