Boost.Preprocessor
C++*
Препроцессор — штука достаточно неодназначная, что-бы это понять, достаточно поднять дискуссию на эту тему на каком-нибудь формуе, и опасная, так-как он вообще не в курсе синтаксиса языка программирования С++, ничего не знает о пространствах имен, шаблонах и тд. Лучше всего ограничить его применение условной компиляцией, а так-же не забывать использовать #undef. Но, если не следовать этой рекомендации, то с его помощью можно делать очень интересные вещи:)
Это можно продемонстрировать на примере списков типов, которые описаны в книге Александресску — «Современное проектирование на С++".
Вот реализация такого списка
То-же самое можно реализовать и без макросов, используя шаблоны:
нечитаемый код:
В этом примере, препроцессор создаст точно такой-же код как и в предидущем примере. Но теперь, в случае если нам потребуется увеличить максимальный размер списка типов для make_typelist, достаточно изменить MAX_TYPELIST_SIZE. Многие библиотеки boost используют подобную технику для эмуляции шаблонов с переменным количеством параметров, например boost.tuple.
В данном конкретном случае, макрос GEN_MAKETYPELIST создает одну из специализаций шаблонного класса make_typelist, в качестве параметров он получает максимальный размер списка типов(n), и количество параметров шаблона(i). Этот макрос используется в BOOST_PP_REPEAT_FROM_TO, для генерации всех необходимых специализаций шаблона.
Сама библиотека boost.preprocessor — достаточно простая. Начать ее использовать очень легко, главное знать, для чего она может быть полезна.
Это можно продемонстрировать на примере списков типов, которые описаны в книге Александресску — «Современное проектирование на С++".
Вот реализация такого списка
а использовать его можно так:
- struct empty_t {};
- template<class Head, class Tail>
- struct cons
- {
- typedef Head head;
- typedef Tail tail;
- };
Но это совсем не удобно, что-бы это исправить, нужен более простой способ объявления списков типов. Самый простой способ это сделать — макросы a-la Александресску:
- typedef cons< int, cons<long, cons< double, empty_t > > > my_typelist;
Это просто и понятно, но не очень удобно, хотелось-бы избавиться от размера списка в имени макроса.
- #define TYPE_LIST_1(t) cons< t, empty_t >
- #define TYPE_LIST_2(t1, t2) cons< t1, TYPE_LIST_1(t2) >
- #define TYPE_LIST_3(t1, t2, t3) cons< t1, TYPE_LIST_2(t2, t3) >
- typedef TYPE_LIST_3(int, long, double) my_typelist;
То-же самое можно реализовать и без макросов, используя шаблоны:
теперь достаточно написать:
- template<class T1, class T2 = empty_t, class T3 = empty_t, class T4 = empty_t>
- struct make_typelist;
- template<class T1>
- struct make_typelist<T1, empty_t, empty_t, empty_t>
- {
- typedef cons<T1, empty_t> type;
- };
- template<class T1, class T2>
- struct make_typelist<T1, T2, empty_t, empty_t>
- {
- typedef cons<T1, cons<T2, empty_t> > type;
- };
- template<class T1, class T2, class T3>
- struct make_typelist<T1, T2, T3, empty_t>
- {
- typedef cons<T1, cons<T2, cons<T3, empty_t> > > type;
- };
и у нас есть список типов. В этом коде мы сталкиваемся с одним из недостатков языка С++ — отсутствием шаблонов с переменным числом параметров, поэтому приходится описывать все необходимые специализации. Страшно представить, что будет, если нам потребуется создать список хотя-бы из десяти элементов. И вот здесь нам может помочь библиотека boost.preprocessor. С ее помощью можно написать вот такой
- typedef make_typelist<int, long, double>::type my_typelist;
- #include <boost/preprocessor/repetition.hpp>
- //boost preprocessor
- #define MAX_TYPELIST_SIZE 4
- template< BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(MAX_TYPELIST_SIZE, class T, empty_t)>
- struct make_typelist;
- #define TUPLE_PRINT(n, i, data) data
- #define GEN_MAKETYPELIST(n, i, unused) \
- template< BOOST_PP_ENUM_PARAMS(i, class T) > \
- struct make_typelist< \
- BOOST_PP_ENUM_PARAMS(i,T) \
- BOOST_PP_COMMA_IF(i) \
- BOOST_PP_ENUM( \
- BOOST_PP_SUB(MAX_TYPELIST_SIZE,i), TUPLE_PRINT, empty_t) > \
- { \
- typedef BOOST_PP_ENUM_PARAMS(i, cons<T), empty_t \
- BOOST_PP_REPEAT(i, TUPLE_PRINT, > ) type; \
- };
- BOOST_PP_REPEAT_FROM_TO(1, MAX_TYPELIST_SIZE, GEN_MAKETYPELIST, ~)
- #undef TUPLE_PRINT
- #undef GEN_MAKETYPELIST
- typedef make_typelist<int, long, double>::type my_typelist;
В этом примере, препроцессор создаст точно такой-же код как и в предидущем примере. Но теперь, в случае если нам потребуется увеличить максимальный размер списка типов для make_typelist, достаточно изменить MAX_TYPELIST_SIZE. Многие библиотеки boost используют подобную технику для эмуляции шаблонов с переменным количеством параметров, например boost.tuple.
В данном конкретном случае, макрос GEN_MAKETYPELIST создает одну из специализаций шаблонного класса make_typelist, в качестве параметров он получает максимальный размер списка типов(n), и количество параметров шаблона(i). Этот макрос используется в BOOST_PP_REPEAT_FROM_TO, для генерации всех необходимых специализаций шаблона.
Сама библиотека boost.preprocessor — достаточно простая. Начать ее использовать очень легко, главное знать, для чего она может быть полезна.

комментарии (30)