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

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

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

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

Ответ 1

Ваш компилятор, вероятно, предоставляет возможность только предварительно обрабатывать, а не компилировать. Это полезно, если вы пытаетесь найти проблему в макросе. Например, используя 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) не был заменен на предварительный процессор, поэтому он попадает в источник в качестве вызова функции undefined.

Ответ 2

Макросы напрямую не рекурсивно расширяются, но существуют обходные пути. Когда препроцессор сканирует и расширяет 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/С++.

Ответ 3

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

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

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

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

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

Рекурсивные макросы не разрешены в C или на С++.

Ответ 4

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

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

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

Ваш звонок:

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

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

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

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

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

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

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

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

Ответ 5

У вас не может быть рекурсивных макросов на C или С++.