2016-08-31 16 views
7

używam zmiennej liczbie argumentów szablony do wdrożenia odwiedzający:Tworzenie aliasu do listy typów i przepuszczenie go jako parametr szablonu

template<typename... Types> 
class Visitor; 

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

template<typename Type, typename... Types> 
class Visitor<Type, Types...>: public Visitor<Types...> { 
    public: 
     using Visitor<Types...>::visit; 

     virtual void visit(Type &visitable) = 0; 
}; 


template<typename... Types> 
class VisitableInterface { 
    public: 
     virtual void accept(Visitor<Types...> &visitor) = 0; 
}; 

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

class IntegerElement; 
class StringElement; 
class BoxElement; 
class ImageElement; 

class IntegerElement: public Visitable<IntegerElement, IntegerElement, StringElement, BoxElement, 
    ImageElement> {}; 

class StringElement: public Visitable<StringElement, IntegerElement, StringElement, BoxElement, 
    ImageElement> {}; 

class BoxElement: public Visitable<BoxElement, IntegerElement, StringElement, BoxElement, 
    ImageElement> {}; 

class ImageElement: public Visitable<ImageElement, IntegerElement, StringElement, BoxElement, 
    ImageElement> {}; 

class RenderEngine : public Visitor<IntegerElement, StringElement, BoxElement, ImageElement> 
{ 
    virtual void visit(IntegerElement& e) {}; 
    virtual void visit(StringElement& e) {}; 
    virtual void visit(BoxElement& e) {}; 
    virtual void visit(ImageElement& e) {}; 
}; 

int main(void) 
{ 
    RenderEngine renderEngine; 
    return 0; 
} 

Zakładając, że będzie więcej klas, które są visitable, możesz skończyć z bardzo długa lista typów dziedziczących po szablonach Visitable i Visitor. Ponadto, jeśli chcesz dodać LinkElement do sprawdzalnych typów akceptowanych przez tego rodzaju odwiedzających, musisz dodać go wszędzie.

Ponieważ ta sama lista typów jest używana przy dziedziczeniu z Visitor i Visitable (z wyjątkiem tego, że bierze się typ dodawania, rodzaj klasy, która z niego dziedziczy), chciałbym wprowadzić bardziej eleganckie rozwiązanie .

Czy istnieje bardziej preferowany i czystszy sposób definiowania aliasu dla tej listy typów innych niż makro?

Uwaga: od makro Mam na myśli definiowania definiowania i używania tego zamiast rzeczywistej listy:

#define VISITABLE_TYPES IntegerElement, StringElement, BoxElement, ImageElement 
// Add more types here 
+0

Co jest powodem down-głosowania? – Nikopol

+0

Zakładam, że przykładowy kod sprawiłby, że pytanie byłoby nieco lepsze. Czasami łatwiej jest zobaczyć kod, o którym mowa. Również, jeśli kod byłby w stanie skompilować to byłoby również miłe, więc możemy dwukrotnie sprawdzić przeciwko kompilatorowi, najpierw bez konieczności kodowania czegoś, co może być w jaki sposób zamierzacie. – Hayt

+0

Okey, dodam minimalny kod, aby wyjaśnić pytanie. Dzięki za opinie! :) – Nikopol

Odpowiedz

1

std::tuple i using są twoi przyjaciele.

Jeśli zdefiniujemy Visitable w ten sposób

template <typename, typename> 
class Visitable; 

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

i dodać, poprzez using, coś, co zastąpi makro pomysł

using tupleT = std::tuple<IntegerElement, StringElement, BoxElement, ImageElement>; 

definicja swoimi elementami stają się po prostu

class IntegerElement: public Visitable<IntegerElement, tupleT> {}; 
class StringElement: public Visitable<StringElement, tupleT> {}; 
class BoxElement: public Visitable<BoxElement, tupleT> {}; 
class ImageElement: public Visitable<ImageElement, tupleT> {}; 

Twój przykład zmodyfikowany

#include <iostream> 

template<typename... Types> 
class Visitor; 

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

template<typename Type, typename... Types> 
class Visitor<Type, Types...>: public Visitor<Types...> { 
    public: 
     using Visitor<Types...>::visit; 

     virtual void visit(Type &visitable) = 0; 
}; 


template<typename... Types> 
class VisitableInterface { 
    public: 
     virtual void accept(Visitor<Types...> &visitor) = 0; 
}; 

template <typename, typename> 
class Visitable; 

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

class IntegerElement; 
class StringElement; 
class BoxElement; 
class ImageElement; 

using tupleT = std::tuple<IntegerElement, StringElement, BoxElement, ImageElement>; 

class IntegerElement: public Visitable<IntegerElement, tupleT> {}; 
class StringElement: public Visitable<StringElement, tupleT> {}; 
class BoxElement: public Visitable<BoxElement, tupleT> {}; 
class ImageElement: public Visitable<ImageElement, tupleT> {}; 

class RenderEngine : public Visitor<IntegerElement, StringElement, BoxElement, ImageElement> 
{ 
    public: 
    virtual void visit(IntegerElement& e) { std::cout << "visit Int\n"; }; 
    virtual void visit(StringElement& e) { std::cout << "visit Str\n"; }; 
    virtual void visit(BoxElement& e) { std::cout << "visit Box\n"; }; 
    virtual void visit(ImageElement& e) { std::cout << "visit Img\n"; }; 
}; 

int main(void) 
{ 
    RenderEngine renderEngine; 

    IntegerElement intE; 
    StringElement strE; 
    BoxElement  boxE; 
    ImageElement imgE; 

    renderEngine.visit(intE); 
    renderEngine.visit(strE); 
    renderEngine.visit(boxE); 
    renderEngine.visit(imgE); 
    return 0; 
} 

--- EDIT ---

Staram się odpowiadać na pytania comment-

dlaczego klasa szablon visitable; potrzebne przed zdefiniowaniem rzeczywistego szablonu?

Nie wiem, czy można to zrobić w prostszy sposób, ale ... ponieważ potrzebujemy "wyodrębnić" typy z std::tuple. Więc trzeba ogólną definicję (template <typename, typename> aby móc odbierać typ std::tuple<something> i trzeba specjalizacji, dzięki czemu można wyodrębnić typy someting.

samo schludny trik może być również wykonane na szablonie Visitor przez definiowania dodatkowy szablon, który zajmuje std :: krotki jako szablon parametru. można dodać do Twojej odpowiedzi, jak również, proszę?

Tak, jest to możliwe.

Ale trzeba modif y VisitableInterface i RenderEngine też.

Duża zmiana dla niewielkiej poprawy (IMHO); tylko do użytku tupleT definiowanie RenderEngine.

W każdym razie, Twój przykład stać

#include <iostream> 

template<typename> 
class Visitor; 

template<typename Type> 
class Visitor<std::tuple<Type>> { 
    public: 
     virtual void visit(Type &visitable) = 0; 
}; 

template<typename Type, typename... Types> 
class Visitor<std::tuple<Type, Types...>>: public Visitor<std::tuple<Types...>> { 
    public: 
     using Visitor<std::tuple<Types...>>::visit; 

     virtual void visit(Type &visitable) = 0; 
}; 

template<typename... Types> 
class VisitableInterface { 
    public: 
     virtual void accept(Visitor<std::tuple<Types...>> &visitor) = 0; 
}; 

template <typename, typename> 
class Visitable; 

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

class IntegerElement; 
class StringElement; 
class BoxElement; 
class ImageElement; 

using tupleT = std::tuple<IntegerElement, StringElement, BoxElement, ImageElement>; 

class IntegerElement: public Visitable<IntegerElement, tupleT> {}; 
class StringElement: public Visitable<StringElement, tupleT> {}; 
class BoxElement: public Visitable<BoxElement, tupleT> {}; 
class ImageElement: public Visitable<ImageElement, tupleT> {}; 

class RenderEngine : public Visitor<tupleT> 
{ 
    public: 
    virtual void visit(IntegerElement& e) { std::cout << "visit Int\n"; }; 
    virtual void visit(StringElement& e) { std::cout << "visit Str\n"; }; 
    virtual void visit(BoxElement& e) { std::cout << "visit Box\n"; }; 
    virtual void visit(ImageElement& e) { std::cout << "visit Img\n"; }; 
}; 

int main(void) 
{ 
    RenderEngine renderEngine; 

    IntegerElement intE; 
    StringElement strE; 
    BoxElement  boxE; 
    ImageElement imgE; 

    renderEngine.visit(intE); 
    renderEngine.visit(strE); 
    renderEngine.visit(boxE); 
    renderEngine.visit(imgE); 
    return 0; 
} 
+0

Ładnie zrobione! Mam jednak jedno pytanie, dlaczego był "szablon klasa Wizyta \" potrzebna przed zdefiniowaniem rzeczywistego szablonu? – Nikopol

+0

Nawiasem mówiąc, tę samą zgrabną sztuczkę można również zrobić dla szablonu Visitor, definiując dodatkowy szablon, który przyjmuje parametr '' std :: tuple'' jako parametr szablonu. Czy możesz to również dodać do swojej odpowiedzi? – Nikopol

+0

@Nikopol - zmodyfikowałem moją odpowiedź, próbując odpowiedzieć na twoje pytania do komentarzy. – max66