Можем ли мы иметь рекурсивные макросы?

Я хочу знать, можем ли мы иметь рекурсивные макросы в C / C ++? Если да, приведите пример.

Второе: почему я не могу выполнить приведенный ниже код? Какую ошибку я делаю? Это из-за рекурсивных макросов?

# define pr(n) ((n==1)? 1 : pr(n-1))
void main ()
{
int a=5;
cout<<"result: "<< pr(5) <<endl;
getch();
}

46

Решение

Ваш компилятор, вероятно, предоставляет возможность только предварительной обработки, а не фактической компиляции. Это полезно, если вы пытаетесь найти проблему в макросе. Например, используя g++ -E:

> g++ -E recursiveMacro.c

# 1 "recursiveMacro.c"# 1 "<built-in>"# 1 "<command line>"# 1 "recursiveMacro.c"
void main ()
{
int a=5;
cout<<"result: "<< ((5==1)? 1 : pr(5 -1)) <<endl;
getch();
}

Как видите, это не рекурсивно. pr(x) заменяется только один раз во время предварительной обработки. Важно помнить, что все, что делает препроцессор, слепо заменяет одну текстовую строку другой, на самом деле он не оценивает такие выражения, как (x == 1),

Причина, по которой ваш код не будет компилироваться, заключается в том, что pr(5 -1) не был заменен препроцессором, поэтому он заканчивается в источнике как вызов неопределенной функции.

15

Другие решения

Макросы не расширяются напрямую, но есть обходные пути. Когда препроцессор сканирует и расширяет pr(5):

pr(5)
^

он создает отключающий контекст, так что когда он видит pr снова:

((5==1)? 1 : pr(5-1))
^

он становится окрашенным в синий цвет и больше не может расширяться, что бы мы ни пытались. Но мы можем предотвратить окрашивание нашего макроса в синий цвет с помощью отложенных выражений и некоторой косвенности:

# define EMPTY(...)
# define DEFER(...) __VA_ARGS__ EMPTY()
# define OBSTRUCT(...) __VA_ARGS__ DEFER(EMPTY)()
# define EXPAND(...) __VA_ARGS__

# define pr_id() pr
# define pr(n) ((n==1)? 1 : DEFER(pr_id)()(n-1))

Так что теперь он будет расширяться так:

pr(5) // Expands to ((5==1)? 1 : pr_id ()(5 -1))

Что идеально, потому что pr никогда не был окрашен в синий цвет. Нам просто нужно применить еще одно сканирование, чтобы расширить его:

EXPAND(pr(5)) // Expands to ((5==1)? 1 : ((5 -1==1)? 1 : pr_id ()(5 -1 -1)))

Мы можем применить два сканирования, чтобы расширить его:

EXPAND(EXPAND(pr(5))) // Expands to ((5==1)? 1 : ((5 -1==1)? 1 : ((5 -1 -1==1)? 1 : pr_id ()(5 -1 -1 -1))))

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

Сначала макрос, чтобы применить много сканов:

#define EVAL(...)  EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__)))
#define EVAL5(...) __VA_ARGS__

Далее макрос concat, который полезен для сопоставления с образцом:

#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__

Счетчики приращения и убывания:

#define INC(x) PRIMITIVE_CAT(INC_, x)
#define INC_0 1
#define INC_1 2
#define INC_2 3
#define INC_3 4
#define INC_4 5
#define INC_5 6
#define INC_6 7
#define INC_7 8
#define INC_8 9
#define INC_9 9

#define DEC(x) PRIMITIVE_CAT(DEC_, x)
#define DEC_0 0
#define DEC_1 0
#define DEC_2 1
#define DEC_3 2
#define DEC_4 3
#define DEC_5 4
#define DEC_6 5
#define DEC_7 6
#define DEC_8 7
#define DEC_9 8

Некоторые макросы, полезные для условных выражений:

#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)

#define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x))
#define NOT_0 ~, 1,

#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0

#define BOOL(x) COMPL(NOT(x))

#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t

#define IF(c) IIF(BOOL(c))

#define EAT(...)
#define EXPAND(...) __VA_ARGS__
#define WHEN(c) IF(c)(EXPAND, EAT)

Собрав все это вместе, мы можем создать повторный макрос:

#define REPEAT(count, macro, ...) \
WHEN(count) \
( \
OBSTRUCT(REPEAT_INDIRECT) () \
( \
DEC(count), macro, __VA_ARGS__ \
) \
OBSTRUCT(macro) \
( \
DEC(count), __VA_ARGS__ \
) \
)
#define REPEAT_INDIRECT() REPEAT

//An example of using this macro
#define M(i, _) i
EVAL(REPEAT(8, M, ~)) // 0 1 2 3 4 5 6 7

Так что да, с некоторыми обходными путями вы можете использовать рекурсивные макросы в C / C ++.

97

Вы не предполагаемый иметь рекурсивные макросы в C или C ++.

Соответствующий язык из стандарта C ++, раздел 16.3.4, параграф 2:

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

На этом языке есть место для маневра. С несколькими макросами, которые вызывают друг друга, есть серая область, где эта формулировка не совсем говорит, что должно быть сделано. Существует активная проблема против стандарта C ++ относительно этой языковой проблемы юриста; увидеть http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#268 .

Игнорируя эту проблему юриста языка, каждый поставщик компилятора понимает намерение:

Рекурсивные макросы не допускаются ни в C, ни в C ++.

17

Скорее всего, вы не можете выполнить его, потому что не можете его скомпилировать. Кроме того, если он будет компилироваться правильно, он всегда будет возвращать 1. Вы имели в виду (n==1)? 1 : n * pr(n-1),

Макросы не могут быть рекурсивными. Согласно главе 16.3.4.2 (спасибо Loki Astari), если текущий макрос найден в списке замен, он остается как есть, таким образом, ваш pr в определении не будет изменено:

Если имя заменяемого макроса найдено во время этого сканирования
список замены (не включая остальную часть
обработка токенов), он не заменяется. Далее, если есть вложенные
замены встречаются с именем заменяемого макроса, это не
заменены. Эти не замененные токены предварительной обработки имен макросов
больше доступны для дальнейшей замены, даже если они позже
(повторно) проверено в контексте, в которых токен предварительной обработки имени макроса
в противном случае был бы заменен.

Ваш звонок:

cout<<"result: "<< pr(5) <<endl;

был преобразован препроцессором в:

cout<<"result: "<< (5==1)? 1 : pr(5-1) <<endl;

Во время этого определения pr макрос ‘потерян’, и компилятор показывает ошибку типа «pr» не был объявлен в этой области (факт) «, потому что нет функции с именем pr,

Использование макросов не приветствуется в C ++. Почему бы тебе просто не написать функцию?

В этом случае вы могли бы даже написать шаблонную функцию, чтобы она разрешалась во время компиляции и работала как постоянное значение:

template <int n>
int pr() {  pr<n-1>(); }

template <>
int pr<1>() { return 1; }
10

Вы не можете иметь рекурсивные макросы в C или C ++.

1
По вопросам рекламы [email protected]