Исходные данные здесь те же что и в предыдущей статье.
struct Base1 {}; struct Base2 {}; struct Base3 {}; struct Derived12: public Base1, public Base2 {}; struct Derived23: public Base2, public Base3 {};
В реальности, у нас и базовых структур, и сообщений, созданных на их основе, гораздо больше.
В самом простом варианте для boost::mpl::for_each нужно указать в качестве шаблонного параметра — список типов, а в качестве аргумента — класс с методом operator()(T), где T — тип из списка. Можно сделать метод шаблонным, но это не совсем то что нам нужно. Поэтому перегрузим operator() для всех базовых структур. Список типов пока в ручную объявим в каждом сообщении. В первом приближении, получаем:
struct Derived12: public Base1, public Base2 { boost::mpl::vector<Base1, Base2> types; }; struct Derived23: public Base2, public Base3 { boost::mpl::vector<Base2, Base3> types; }; class Describer { public: void operator()(Base1) { std::cout << "Получение информации из Base1\n"; } void operator()(Base2) { std::cout << "Получение информации из Base2\n"; } void operator()(Base3) { std::cout << "Получение информации из Base3\n"; } }; void main() { Derived12 d12; boost::for_each<Derived12::types>(Describer()); }
В результате исполнения будет
Получение информации из Base1 Получение информации из Base2
У такого ваианта есть две проблемы:
- Значение d12 никак не используется;
- Функция boost::mpl::for_each создает экземпляры базовых структур.
С первой проблемой все просто — пусть значение передается и сохраняется на конструкторе Describer, а сам класс будет шаблонным. Вторая проблема более серьезная, так как кроме затрат на создание объектов дополнительно накладываются ограничения на структуры — они должны иметь конструктор без параметров и не могут быть абстрактными. Я решил, перегрузить operator() по указателю. В этом случае список типов должен содержать указатели на типы или можно воспользоваться вторым вариантом for_each, с передачей шаблона для трансформации, этим вариантом и воспользуемся. В качестве шаблона для трансформации возьмем boost::add_pointer. Для проверки что обрабатываются все базовые структуры добавим шаблонный operator(), содержащий BOOST_STATIC_ASSERT(false). Это даст ошибку компиляции если появится новая базовая структура. В итоге получим:
template<typename T> class Describer { public: Describer(const T& v): v(v) {} void operator()(Base1*) { std::cout << "Получение информации из Base1\n"; } void operator()(Base2*) { std::cout << "Получение информации из Base2\n"; } void operator()(Base3*) { std::cout << "Получение информации из Base3\n"; } template<typename U> void operator()(U*) { BOOST_STATIC_ASSERT(false); } private: const T& v; }; void main() { Derived12 d12; boost::for_each< Derived12::types, boost::add_pointer<boost::mpl::_> > ( Describer<Derived12>(d12) ); }
Теперь попробуем упростить заведение списков типов, участвующих в наследовании. Объявим полный список типов базовых структур и воспользуемся алгоритмом boost::mpl::copy_if. Который скопирует в новый список все элементы, удовлетворяющие указанному условию. В качестве условия возьмем проверку на наследование boost::is_base_of.
typedef boost::mpl::vector<Base1, Base2, Base3> FullTypesList; template<typename T, typename BaseList> struct MakeTypesList { typedef typename boost::mpl::copy_if< BaseList, boost::is_base_of< boost::mpl::_, T > >::type TypesList; };
Для удобства добавим в Describer operator() без параметров, который будет вызывать for_each.
void Describer::operator()() { boost::mpl::for_each< typename MakeTypesList<T,FullTypesList>::TypesList, add_pointer<boost::mpl::_> >( boost::ref(*this) ); }
Обертка boost::ref нужна, чтобы не вызвался оператор копирования для Describer.
struct Base1 {}; struct Base2 {}; struct Base3 {}; typedef boost::mpl::vector<Base1, Base2, Base3> FullTypesList; template<typename T, typename BaseList> struct MakeTypesList { typedef typename boost::mpl::copy_if< BaseList, boost::is_base_of< boost::mpl::_, T > >::type TypesList; }; template<typename T> class Describer { public: Describer(const T& v): v(v) {} void operator()() { boost::mpl::for_each< typename MakeTypesList<T,FullTypesList>::TypesList, add_pointer<boost::mpl::_> >( boost::ref(*this) ); } void operator()(Base1*) { std::cout << "Получение информации из Base1\n"; } void operator()(Base2*) { std::cout << "Получение информации из Base2\n"; } void operator()(Base3*) { std::cout << "Получение информации из Base3\n"; } template<typename U> void operator()(U*) { BOOST_STATIC_ASSERT(false); } private: const T& v; }; //Списки типов в Derived12 и Derived23 больше не нужны. struct Derived12: public Base1, public Base2 {}; struct Derived23: public Base2, public Base3 {}; void main() { Derived12 mes12; ( Describer<Derived12>(mes12) )(); }
Если классов обрабатывающих структуры подобным образом много, то разумнее объявить списки базовых классов для сообщений отдельно. У нас получилось, что структура сообщения не используется самостоятельно — она является базовым классом для шаблонного класса, реализующего общий интерфейс всех сообщений и в нем мы определяем список базовых типов. К этому списку и обращаемся при вызове for_each. Можно сделать шаблон-обертку и использовать его. Простой вариант
template<typename T> struct Wrapper: public T { typedef typename MakeTypesList<T, FullTypesList>::TypesList TypesList; } void Describer::operator() { boost::mpl::for_each< typename T::TypesList, add_pointer<boost::mpl::_> >( boost::ref(*this) ); }
ссылка на оригинал статьи http://habrahabr.ru/post/247365/
Добавить комментарий