Препроцессор - штука достаточно неодназначная, что-бы это понять, достаточно поднять дискуссию на эту тему на каком-нибудь формуе, и опасная, так-как он вообще не в курсе синтаксиса языка программирования С++, ничего не знает о пространствах имен, шаблонах и тд. Лучше всего ограничить его применение условной компиляцией а так-же не забывать использовать #undef. Но, если не следовать этой рекомендации, то с его помощью можно делать очень интересные вещи :)
Это можно продемонстрировать на примере списков типов, которые описаны в книге Александресску - "Современное проектирование на С++".
Вот реализация такого списка
struct empty_t {};
template<class Head, class Tail>
struct cons
{
typedef Head head;
typedef Tail tail;
};
а использовать его можно так:
typedef cons< int, cons<long, cons< double, empty_t > > > tl_first;
Но это совсем не удобно, что-бы это исправить, нужен более простой способ объявления списков типов.
Самый простой способ это сделать - макросы a-la Александресску:#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) tl_second;
Это просто и понятно, но не очень удобно, хотелось-бы избавиться от размера списка в имени макроса.
То-же самое можно реализовать и без макросов, используя шаблоны: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;
};
теперь достаточно написать:typedef make_typelist<int, long, double>::type tl_third;
и у нас есть список типов. В этом коде мы сталкиваемся с одним из недостатков языка С++ - отсутствием шаблонов с переменным числом параметров, поэтому приходится описывать все необходимые специализации. Страшно представить, что будет, если нам потребуется создать список хотя-бы из десяти элементов. И вот здесь нам может помочь библиотека boost.preprocessor. С ее помощью можно написать вот такой нечитаемый код:#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, ~)
typedef make_typelist<int, long, double>::type tl_third;
#undef TUPLE_PRINT
#undef GEN_MAKETYPELIST
В этом примере, препроцессор создаст точно такой-же код как и в предидущем примере. Но теперь, в случае если нам потребуется увеличить максимальный размер списка типов для make_typelist, достаточно изменить MAX_TYPELIST_SIZE. Многие библиотеки boost используют подобную технику для эмуляции шаблонов с переменным количеством параметров, например boost.tuple.
В данном конкретном случае, макрос GEN_MAKETYPELIST создает одну из специализаций шаблонного класса make_typelist, в качестве параметров он получает максимальный размер списка типов(n), и количество параметров шаблона(i). Этот макрос используется в BOOST_PP_REPEAT_FROM_TO, для генерации всех необходимых специализаций шаблона.
Сама библиотека boost.preprocessor - достаточно простая. Начать ее использовать очень легко, главное знать, для чего она может быть полезна.