2016-08-31 12 views
7

Я использую VARIADIC шаблонов для реализации шаблона посетителя:Создать псевдоним для списка типов и передать его в качестве параметра шаблона

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

Предполагая, что там будут больше классов, которые посещаемые, вы в конечном итоге с очень длинный список типов при наследовании от шаблонов Visitable и Visitor. Кроме того, если вы хотите добавить LinkElement к посещаемым типам, принятым этим посетителем, вы должны добавить его повсюду.

Поскольку тот же список типов используется при наследовании от Visitor и Visitable (за исключением того, что этот тип принимает тип добавления, тип наследуемого от него класса), я хотел бы реализовать более элегантное решение ,

Есть ли более предпочтительный, более чистый способ определить псевдоним для этого списка типов, кроме макроса?

Примечание: макросами я имею в виду определение определения и использования этого вместо фактического списка:

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

Что является причиной вниз голосования? – Nikopol

+0

Я предполагаю, что пример кода поставит вопрос немного лучше. Иногда легче увидеть этот код. Также, если код мог бы скомпилировать это, также было бы хорошо, поэтому мы могли бы сначала проверить перед компилятором, не имея необходимости кодировать что-то, что может быть так, как вы его намереваетесь. – Hayt

+0

Okey, я добавлю минимальный код, чтобы прояснить вопрос. Спасибо за ответ! :) – Nikopol

ответ

1

std::tuple и using ваши друзья.

Если вы определяете Visitable таким образом

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

и добавить через using, то, что заменить идею макросъемки

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

Ваш пример изменен

#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 ---

Я стараюсь отвечать на Ваш комментарий-вопросы

почему класс шаблон Visitable; необходимо задать , прежде чем определять фактический шаблон?

Я не знаю, возможно ли это сделать проще, но ... это потому, что нам нужно «извлечь» типы из std::tuple. Так что вам нужно общее определение (template <typename, typename>, чтобы иметь возможность получить std::tuple<something> тип и вам нужна специализация, так что вы можете извлечь someting типов.

такой же ловкий трюк может быть также сделано для шаблона Visitor путем определения дополнительный шаблон, который принимает зЬй :: кортежа в качестве шаблона параметра. вы можете добавить это к вашему ответу, а также, пожалуйста?

Да, это возможно.

Но вы должны MODIF y VisitableInterface и RenderEngine тоже.

Большое изменение для небольшого улучшения (ИМХО); только для использования tupleT, определяющий RenderEngine.

Во всяком случае, ваш пример стал

#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

Красиво сделано! У меня есть один вопрос, почему, почему перед тем, как определить фактический шаблон, был 'template class Visitable;' – Nikopol

+0

Кстати, такой же опрятный трюк можно также сделать для шаблона посетителя, указав дополнительный шаблон, который принимает параметр '' std :: tuple'' в качестве параметра шаблона. Можете ли вы добавить это и к своему ответу, пожалуйста? – Nikopol

+0

@Nikopol - изменил мой ответ, пытаясь ответить на ваши комментарии. – max66