Инкрементные макросы препроцессора - PullRequest
1 голос
/ 17 февраля 2011

Я пытаюсь сделать простой цикл препроцессора. (Я понимаю, что это ужасная идея, ну да ладно.)

// Preprocessor.h

#ifndef PREPROCESSOR_LOOP_ITERATION

#define MAX_LOOP_ITERATION 16 // This can be changed.

#define PREPROCESSOR_LOOP_ITERATION 0

#endif

#if (PREPROCESSOR_LOOP_ITERATION < MAX_LOOP_ITERATION)
#define PREPROCESSOR_LOOP_ITERATION (PREPROCESSOR_LOOP_ITERATION + 1) // Increment PREPROCESSOR_LOOP_ITERATION.
#include "Preprocessor.h"
#endif

Проблема в том, что он не похож на PREPROCESSOR_LOOP_ITERATION, который увеличивается, поэтому он просто продолжает бесконечно включать себя. Если я изменю строку на фактическое целое число (например, 17), препроцессор правильно пропустит директиву #include.

Что я делаю неправильно?

Ответы [ 3 ]

7 голосов
/ 17 февраля 2011

«Проблема» в том, что макросы лениво оцениваются. Рассмотрим определение вашего макроса:

#define PREPROCESSOR_LOOP_ITERATION (PREPROCESSOR_LOOP_ITERATION + 1)

Это определяет макрос с именем PREPROCESSOR_LOOP_ITERATION, а его список замен представляет собой последовательность из пяти токенов предварительной обработки (, PREPROCESSOR_LOOP_ITERATION, +, 1 и ). Макрос не раскрывается в списке замены, когда макрос определен. Замена макроса происходит только при вызове макроса. Рассмотрим более простой пример:

#define A X
#define B A

B // this expands to the token X

#undef A
#define A Y
B // this expands to the token Y

Существует дополнительное правило, согласно которому если имя заменяемого макроса встречается в списке замены, оно не обрабатывается как макрос и, следовательно, не заменяется (это эффективно запрещает рекурсию во время замены макроса). Так что, в вашем случае, каждый раз, когда вы вызываете макрос PREPROCESSOR_LOOP_ITERATION, он заменяется на

( PREPROCESSOR_LOOP_ITERATION + 1 )

затем замена макроса останавливается, и предварительная обработка продолжается со следующим токеном.

Вы можете выполнять ограниченную арифметику с препроцессором, определяя последовательность макросов и используя оператор конкатенации (##), но это довольно утомительно. Вам следует подумать об использовании библиотеки Boost.Preprocessor , чтобы помочь вам в этом. Он будет работать как с C, так и с C ++ кодом. Он допускает ограниченную итерацию, но то, что он допускает, чрезвычайно полезен. Ближайшая функция, которая соответствует вашему варианту использования, вероятно, BOOST_PP_ITERATE. Другие средства, такие как обработчики последовательности (BOOST_PP_SEQ), очень полезны для написания генерирующего кода.

4 голосов
/ 17 февраля 2011

РЕДАКТИРОВАТЬ: Как указал Джеймс, мое оригинальное решение не сработало из-за ленивой оценки макросов.Если ваш компилятор поддерживает его, макрос __COUNTER__ увеличивается на единицу при каждом вызове, и вы можете использовать его для создания простого цикла препроцессора, подобного следующему:

// Preprocessor.h
#define MAX_LOOP_ITERATION 16 // Be careful of off-by-one

// do stuff

#if (__COUNTER__ < MAX_LOOP_ITERATION)
#include "Preprocessor.h"
#endif

Я проверил это в Visual C с помощьюработает cl /P Preprocessor.h.

1 голос
/ 17 февраля 2011

Серьезно, найдите другой способ сделать это.

Препроцессор должен быть включен в состав защиты и простых условных компиляций.

Все остальное, для чего это было полезно, имеет лучший способ сделать это на C ++ (встраивание, шаблоны и т. Д.).

Тот факт, что вы заявляете I realize this is a horrible idea ..., должен быть мертвой раздачей, что вы должны переосмыслить то, что вы делаете: -)

Я хотел бы предложить вам сделать шаг назад и рассказать нам о реальной проблеме, которую вы пытаетесь решить. Я подозреваю, что реализация рекурсивных макросов - это не проблема, а средство для решения проблемы, с которой вы столкнулись. Знание коренной проблемы откроет множество других чудесных возможностей.

...