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

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

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

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

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

 > g++ -E recursiveMacro.c # 1 "recursiveMacro.c" # 1 "" # 1 "" # 1 "recursiveMacro.c" void main () { int a=5; cout<<"result: "<< ((5==1)? 1 : pr(5 -1)) < 

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

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

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

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

Сначала макрос для применения большого количества сканирований:

 #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 

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

 #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 

Итак, да, с некоторыми обходными решениями вы можете иметь рекурсивные macros в C / C ++.

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

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

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

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

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

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

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

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

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

Ваш звонок:

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

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

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

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

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

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

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

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

  • C многострочный макрос: do / while (0) vs scope block
  • Как определить платформу / компилятор из макросов препроцессора?
  • Использование X-Macros в реальном мире
  • Вариадические рекурсивные macros препроцессора - возможно ли это?
  • Найти (и убить) процесс блокировки порта 3000 на Mac
  • Являются ли typedef и #define одинаковыми в c?
  • Interesting Posts

    как поместить текст слева от переключателя в андроиде

    Время с момента последней разблокировки Windows 7

    Как использовать 3-значные цветовые коды, а не 6-значные цветовые коды в CSS?

    Ошибка установки SSDT (Не удалось выполнить пакет EXE.)

    SubSonic ASP.NET MVC-образец в Visual Web Developer Express

    Почему изменение разрешения ЖК-экрана (Windows 7) занимает несколько секунд?

    Как добавить «Библиотека поддержки Android Design» в Eclipse с помощью ADT-плагина?

    Как получить доступ к журналу вызовов для Android?

    gwt – Использование списка в вызове RPC?

    Комбинации клавиш Remap в Windows

    Инструмент для установки установленных компонентов на ПК

    Существуют ли отдельные процессоры для серверов?

    Как изменить современный пользовательский интерфейс Skype на обычное окно

    Почему строки неизменны во многих языках программирования?

    Автоматически менять громкость системы при подключении аудиоустройства?

    Давайте будем гением компьютера.