- PVSM.RU - https://www.pvsm.ru -
Хочу рассказать как мы использовали списки базовых типов для обработки сообщений. Сообщения представляют собой структуры, унаследованные от небольших базовых структур. Вся полезная информация хранится в базовых структурах. Для обработки нужно знать от каких базовых структур было унаследовано обрабатываемое сообщение. Все что нужно для работы со списками типов мы нашли в Boost.MPL. В качестве списка типов выбрали boost::mpl::vector [1]. Для прохода по списку типов boost::mpl::for_each [2].
Исходные данные здесь те же что и в предыдущей статье [3].
struct Base1 {};
struct Base2 {};
struct Base3 {};
struct Deliver12: public Base1, public Base2 {};
struct Deliver23: public Base2, public Base3 {};
В реальности, у нас и базовых структур, и сообщений, созданных на их основе, гораздо больше.
В самом простом варианте для boost::mpl::for_each нужно указать в качестве шаблонного параметра — список типов, а в качестве аргумента — класс с методом operator()(T), где T — тип из списка. Можно сделать метод шаблонным, но это не совсем то что нам нужно. Поэтому перегрузим operator() для всех базовых структур. Список типов пока в ручную объявим в каждом сообщении. В первом приближении, получаем:
struct Deliver12: public Base1, public Base2
{
boost::mpl::vector<Base1, Base2> types;
};
struct Deliver23: public Base2, public Base3
{
boost::mpl::vector<Base2, Base3> types;
};
class Describer
{
public:
void operator()(Base1)
{
std::cout << "Получение информации из Base1n";
}
void operator()(Base2)
{
std::cout << "Получение информации из Base2n";
}
void operator()(Base3)
{
std::cout << "Получение информации из Base3n";
}
};
void main()
{
Deliver12 d12;
boost::for_each<Deliver12::types>(Describer());
}
В результате исполнения будет
Получение информации из Base1 Получение информации из Base2
У такого ваианта есть две проблемы:
С первой проблемой все просто — пусть значение передается и сохраняется на конструкторе 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 << "Получение информации из Base1n";
}
void operator()(Base2*)
{
std::cout << "Получение информации из Base2n";
}
void operator()(Base3*)
{
std::cout << "Получение информации из Base3n";
}
template<typename U>
void operator()(U*)
{
BOOST_STATIC_ASSERT(false);
}
private:
const T& v;
};
void main()
{
Deliver12 d12;
boost::for_each< Deliver12::types,
boost::add_pointer<boost::mpl::_> >
( Describer<Deliver12>(d12) );
}
Теперь попробуем упростить заведение списков типов, участвующих в наследовании. Объявим полный список типов базовых структур и воспользуемся алгоритмом boost::mpl::copy_if [4]. Который скопирует в новый список все элементы, удовлетворяющие указанному условию. В качестве условия возьмем проверку на наследование boost::is_base_of [5].
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 << "Получение информации из Base1n";
}
void operator()(Base2*)
{
std::cout << "Получение информации из Base2n";
}
void operator()(Base3*)
{
std::cout << "Получение информации из Base3n";
}
template<typename U>
void operator()(U*)
{
BOOST_STATIC_ASSERT(false);
}
private:
const T& v;
};
//Списки типов в Deliver12 и Deliver23 больше не нужны.
struct Deliver12: public Base1, public Base2 {};
struct Deliver23: public Base2, public Base3 {};
void main()
{
Deliver12 mes12;
( Describer<Deliver12>(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) );
}
Автор: Hokum
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-3/78782
Ссылки в тексте:
[1] boost::mpl::vector: http://www.boost.org/doc/libs/1_57_0/libs/mpl/doc/refmanual/vector.html
[2] boost::mpl::for_each: http://www.boost.org/doc/libs/1_57_0/libs/mpl/doc/refmanual/for-each.html
[3] предыдущей статье: http://habrahabr.ru/post/247081/
[4] boost::mpl::copy_if: http://www.boost.org/doc/libs/1_57_0/libs/mpl/doc/refmanual/copy-if.html
[5] boost::is_base_of: http://www.boost.org/doc/libs/1_57_0/libs/type_traits/doc/html/boost_typetraits/reference/is_base_of.html
[6] Источник: http://habrahabr.ru/post/247365/
Нажмите здесь для печати.