Boost.Preprocessor

C++*
Препроцессор — штука достаточно неодназначная, что-бы это понять, достаточно поднять дискуссию на эту тему на каком-нибудь формуе, и опасная, так-как он вообще не в курсе синтаксиса языка программирования С++, ничего не знает о пространствах имен, шаблонах и тд. Лучше всего ограничить его применение условной компиляцией, а так-же не забывать использовать #undef. Но, если не следовать этой рекомендации, то с его помощью можно делать очень интересные вещи:)
Это можно продемонстрировать на примере списков типов, которые описаны в книге Александресску — «Современное проектирование на С++".
Вот реализация такого списка
  1. struct empty_t {};
  2.  
  3. template<class Head, class Tail>
  4. struct cons
  5. {
  6.     typedef Head head;
  7.     typedef Tail tail;
  8. };

а использовать его можно так:
  1. typedef cons< int, cons<long, cons< double, empty_t > > > my_typelist;

Но это совсем не удобно, что-бы это исправить, нужен более простой способ объявления списков типов. Самый простой способ это сделать — макросы a-la Александресску:
  1. #define TYPE_LIST_1(t) cons< t, empty_t >
  2. #define TYPE_LIST_2(t1, t2) cons< t1, TYPE_LIST_1(t2) >
  3. #define TYPE_LIST_3(t1, t2, t3) cons< t1, TYPE_LIST_2(t2, t3) >
  4. typedef TYPE_LIST_3(int, long, double) my_typelist;

Это просто и понятно, но не очень удобно, хотелось-бы избавиться от размера списка в имени макроса.
То-же самое можно реализовать и без макросов, используя шаблоны:
  1. template<class T1, class T2 = empty_t, class T3 = empty_t, class T4 = empty_t>
  2. struct make_typelist;
  3.  
  4.  
  5. template<class T1>
  6. struct make_typelist<T1, empty_t, empty_t, empty_t>
  7. {
  8.     typedef cons<T1, empty_t> type;
  9. };
  10.  
  11. template<class T1, class T2>
  12. struct make_typelist<T1, T2, empty_t, empty_t>
  13. {
  14.     typedef cons<T1, cons<T2, empty_t> > type;
  15. };
  16.  
  17. template<class T1, class T2, class T3>
  18. struct make_typelist<T1, T2, T3, empty_t>
  19. {
  20.     typedef cons<T1, cons<T2, cons<T3, empty_t> > > type;
  21. };

теперь достаточно написать:
  1. typedef make_typelist<int, long, double>::type my_typelist;

и у нас есть список типов. В этом коде мы сталкиваемся с одним из недостатков языка С++ — отсутствием шаблонов с переменным числом параметров, поэтому приходится описывать все необходимые специализации. Страшно представить, что будет, если нам потребуется создать список хотя-бы из десяти элементов. И вот здесь нам может помочь библиотека boost.preprocessor. С ее помощью можно написать вот такой нечитаемый код:
  1. #include <boost/preprocessor/repetition.hpp>
  2. //boost preprocessor
  3. #define MAX_TYPELIST_SIZE 4
  4.  
  5. template< BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(MAX_TYPELIST_SIZE, class T, empty_t)>
  6. struct make_typelist;
  7.  
  8. #define TUPLE_PRINT(n, i, data) data
  9. #define GEN_MAKETYPELIST(n, i, unused)                                  \
  10. template< BOOST_PP_ENUM_PARAMS(i, class T) >                            \
  11. struct make_typelist<                                                   \
  12.       BOOST_PP_ENUM_PARAMS(i,T)                                         \
  13.       BOOST_PP_COMMA_IF(i)                                              \
  14.       BOOST_PP_ENUM(                                                    \
  15.           BOOST_PP_SUB(MAX_TYPELIST_SIZE,i), TUPLE_PRINT, empty_t) >    \
  16. {                                                                       \
  17.     typedef BOOST_PP_ENUM_PARAMS(i, cons<T), empty_t                    \
  18.             BOOST_PP_REPEAT(i, TUPLE_PRINT, > ) type;                   \
  19. };
  20.  
  21. BOOST_PP_REPEAT_FROM_TO(1, MAX_TYPELIST_SIZE, GEN_MAKETYPELIST, ~)
  22.  
  23. #undef TUPLE_PRINT
  24. #undef GEN_MAKETYPELIST
  25.  
  26. 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 — достаточно простая. Начать ее использовать очень легко, главное знать, для чего она может быть полезна.
+33
2 мая 2009, 19:06
26
Lazin 5,1

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

НЛО прилетело и опубликовало эту надпись здесь
+1
ScREW #
Статья о C++. Не о C.
НЛО прилетело и опубликовало эту надпись здесь
0
Paul #
Под Си я подразумеваю Си/Си++
Это совершенно разные языки.

Вообще в жизни не видел человека который бы программил только на одном из них
Прислать вам свою фотографию? :)
НЛО прилетело и опубликовало эту надпись здесь
+2
ScREW #
Код написаный на C можно скомпилировать компилятором C++.

На этом сходства заканчиваются. С и C++ языки разного уровня абстракции, следовательно на каждом из них решаются совершенно специфические задачи совершенно специфичными способами. Люди которые пишут на языке C думают совсем иначе, нежели люди пишущие на C++ — они говорят не так, как C++ разработчики, одеваются не так как C++ разработчики, есть даже мнение что они спят подвесив себя к потолку вниз головой.

Встретить в природе живого C разработчика теперь не так-то просто, потому что даже микроконтроллеры теперь программируют на C++.

А вот ядро линукс написано на языке C… И это, лично меня, здорово угнетает. Я могу понять даже некоторые части библиотеки boost, но вот ядро линукс осилить, боюсь, не смогу никогда.
НЛО прилетело и опубликовало эту надпись здесь
+1
ScREW #
Есть гаечный ключ на 19, а пневмотический шуруповёрт. (В гонках F1 таким колёса прикручивают) И то и другое ручной инструмент для откручивания болтов. Каждый из этих инструментов хорош по-своему: один можно возить в богажнике, вторым можно открутить все 4 болта за 0.7 секунды.

Что является расширением чего каждый решает для себя.

Я специализируюсь на более продвинутой технике. (Это не значит, что на людей, которые виртуозно владеют низкоуровневыми инструментами я смотрю свысока. Это значит, что я не можу жить без stl и boost)

Вы, я полагаю, не видите огромной пропасти между этими языками вследствии малого опыта работы хотябы с одним из них.
НЛО прилетело и опубликовало эту надпись здесь
0
Paul #
Си++ посути является разширением Си, а потому уже разными они быть не могут, так как имеют общий базис.
У них не общий базис, у них похожий синтаксис, максимум. Совершенно разный подход к программированию, программистам на С часто мешает их багаж и когда они начинают писать на С++ получается вот так.
А ещё программируя на С++
  • Программисты на С не пользуются RAII и соблюдают из-за этого правило одного return, к которому делают goto, чтобы освободить ресурсы в одном месте.
  • Программисты на С не пользуются контейнерами, а пользуются C-style массивами, которые небезопасны и фиг потом найдешь ошибку выхода за границу массива.
  • Программисты на С не пользуются *_cast<>, а пользуются C-style cast'ами типа (Type*) под которым может замаскироваться и const_cast и reinterpret_cast и этого будет не видно, пока не вчитаешься, а вчитываешься обычно когда уже поздно.

Я могу приводить примеры и дальше.
–2
tenshi #
а сделайте препроцессор, чтобы в дефайне можно было писать на руби ;-)
0
Lazin #
А почему именно на руби? )
+2
tenshi #
он первый в голову пришёл =)
0
redchrom #
Такой препроцессор написать пять минут, он будет переписывать блоки на Ruby/Python/Lisp/Etc… и отдавать результат нормальному cpp.
0
tenshi #
ах да… но это ж не спортивно? ;-) брейнфак — наше всё!
+3
shai_xylyd #
А где применяются списки типов?
0
Lazin #
Где только не применяются, самый распространенный в настоящее время варинат — tr1::tuple.
0
nuit #
а где применяется tr1::tuple?
0
rg_software #
«Кортеж» — популярный тип данных в математике, так что в математических пакетах.

В пределах буста — в самых разных библиотеках, например, assign, graph.
+9
redchrom #
Reinventing lisp :)
+3
Tagire #
У меня вообще сложилось впечатление, что лисп это такая асимптота языков программирования.
+1
maifor #
думал вы про гитарный буст процессор говорите. даже все прочитал :)
ничего правда не понял :(
0
romy4 #
а с первых строчек кода непонятно было, что это не для гитары? :)))
+2
maifor #
как-то затянуло :)
+1
ozonebaby #
D поддерживает шаблоны с переменным числом параметров специализации.
+1
ozonebaby #
Неплохое видео кстати, www.vimeo.com/4333802
+1
Lazin #
C++ то-же скоро будет )
0
ozonebaby #
это лишь одна из многочисленных фич D. С++ 0х реализовал лишь несколько, которые в D уже давно. В D2 появились такие фичи, о которых авторы других языков еще только помышляют(автор C# например о контстантности). Или например ranges.
А вообще, смотришь годобные статьи, как только люди не изголяются… Это конечно достойно уважения, найти решение задачи, но достаточно совсем немного поменять условия. Хотя конечно смена языка программирования — это очень серьезный шаг и много от чего зависит.
0
kelvin #
Спасибо, интересная статья.
–1
proxor #
За статью спасибо. Но у вас плохая привычка ставить дефисы там, где они совершенно не нужны. Не надо, пожалуйста.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.