Обработка структуры по списку базовых типов

Хочу рассказать как мы использовали списки базовых типов для обработки сообщений. Сообщения представляют собой структуры, унаследованные от небольших базовых структур. Вся полезная информация хранится в базовых структурах. Для обработки нужно знать от каких базовых структур было унаследовано обрабатываемое сообщение. Все что нужно для работы со списками типов мы нашли в Boost.MPL. В качестве списка типов выбрали boost: mpl: vector. Для прохода по списку типов boost: mpl: for_each.Исходные данные здесь те же что и в предыдущей статье. Скрытый текст 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 types; };

struct Derived23: public Base2, public Base3 { boost: mpl: vector 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(Describer ()); } В результате исполнения будет выведено Получение информации из Base1 Получение информации из Base2 У такого ваианта есть две проблемы: Значение d12 никак не используется; Функция boost: mpl: for_each создает экземпляры базовых структур. С первой проблемой все просто — пусть значение передается и сохраняется на конструкторе Describer, а сам класс будет шаблонным. Вторая проблема более серьезная, так как кроме затрат на создание объектов дополнительно накладываются ограничения на структуры — они должны иметь конструктор без параметров и не могут быть абстрактными. Я решил, перегрузить operator () по указателю. В этом случае список типов должен содержать указатели на типы или можно воспользоваться вторым вариантом for_each, с передачей шаблона для трансформации, этим вариантом и воспользуемся. В качестве шаблона для трансформации возьмем boost: add_pointer. Для проверки что обрабатываются все базовые структуры добавим шаблонный operator (), содержащий BOOST_STATIC_ASSERT (false). Это даст ошибку компиляции если появится новая базовая структура. В итоге получим: template 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 void operator ()(U*) { BOOST_STATIC_ASSERT (false); } private: const T& v; };

void main () { Derived12 d12;

boost: for_each< Derived12::types, boost::add_pointer > (Describer(d12)); } Теперь попробуем упростить заведение списков типов, участвующих в наследовании. Объявим полный список типов базовых структур и воспользуемся алгоритмом boost: mpl: copy_if. Который скопирует в новый список все элементы, удовлетворяющие указанному условию. В качестве условия возьмем проверку на наследование boost: is_base_of.

typedef boost: mpl: vector FullTypesList;

template 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:: TypesList, add_pointer >(boost: ref (*this)); } Обертка boost: ref нужна, чтобы не вызвался оператор копирования для Describer.Окончательный вариант struct Base1 {}; struct Base2 {}; struct Base3 {};

typedef boost: mpl: vector FullTypesList;

template struct MakeTypesList { typedef typename boost: mpl: copy_if< BaseList, boost::is_base_of< boost::mpl::_, T > >:: type TypesList; };

template class Describer { public: Describer (const T& v): v (v) {}

void operator ()() { boost: mpl: for_each< typename MakeTypesList:: TypesList, add_pointer >(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 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(mes12))(); } Если классов обрабатывающих структуры подобным образом много, то разумнее объявить списки базовых классов для сообщений отдельно. У нас получилось, что структура сообщения не используется самостоятельно — она является базовым классом для шаблонного класса, реализующего общий интерфейс всех сообщений и в нем мы определяем список базовых типов. К этому списку и обращаемся при вызове for_each. Можно сделать шаблон-обертку и использовать его. Простой вариант

шаблона-обертки template struct Wrapper: public T { typedef typename MakeTypesList:: TypesList TypesList; }

void Describer: operator () { boost: mpl: for_each< typename T::TypesList, add_pointer >(boost: ref (*this));

}

© Habrahabr.ru