Quiero saber si podemos tener macros recursivas en C/C++. En caso afirmativo, proporcione un ejemplo de ejemplo.
Segunda cosa: ¿por qué no puedo ejecutar el siguiente código? ¿Cuál es el error que estoy haciendo? ¿Es por las macros recursivas?
# define pr(n) ((n==1)? 1 : pr(n-1))
void main ()
{
int a=5;
cout<<"result: "<< pr(5) <<endl;
getch();
}
Las macros no se expanden directamente de forma recursiva, pero existen soluciones alternativas. Cuando el preprocesador escanea y se expande pr(5)
:
pr(5)
^
crea un contexto incapacitante, de modo que cuando ve pr
otra vez:
((5==1)? 1 : pr(5-1))
^
se pinta de azul y ya no puede expandirse, sin importar lo que intentemos. Pero podemos evitar que nuestra macro se pinte de azul usando expresiones diferidas y algo de direccionamiento indirecto:
# 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))
Así que ahora se expandirá así:
pr(5) // Expands to ((5==1)? 1 : pr_id ()(5 -1))
Lo cual es perfecto, porque pr
nunca se pintó de azul. Solo necesitamos aplicar otro escaneo para que se expanda aún más:
EXPAND(pr(5)) // Expands to ((5==1)? 1 : ((5 -1==1)? 1 : pr_id ()(5 -1 -1)))
Podemos aplicar dos escaneos para que se expanda aún más:
EXPAND(EXPAND(pr(5))) // Expands to ((5==1)? 1 : ((5 -1==1)? 1 : ((5 -1 -1==1)? 1 : pr_id ()(5 -1 -1 -1))))
Sin embargo, dado que no existe una condición de terminación, nunca podemos aplicar suficientes escaneos. No estoy seguro de lo que quiere lograr, pero si tiene curiosidad sobre cómo crear macros recursivas, aquí hay un ejemplo de cómo crear una macro de repetición recursiva.
Primero una macro para aplicar muchos escaneos:
#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__
A continuación, una macro concat que es útil para la coincidencia de patrones:
#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
Contadores de incremento y decremento:
#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
Algunas macros útiles para condicionales:
#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)
Poniendo todo junto podemos crear una macro de repetición:
#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
Entonces, sí, con algunas soluciones puede tener macros recursivas en C/C++.
Su compilador probablemente proporcione una opción para solo preprocesar, no compilar en realidad. Esto es útil si está tratando de encontrar un problema en una macro. Por ejemplo usando 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();
}
Como puede ver, no es recursivo. pr(x)
solo se reemplaza una vez durante el preprocesamiento. Lo importante a recordar es que todo lo que hace el preprocesador es reemplazar ciegamente una cadena de texto con otra, en realidad no evalúa expresiones como (x == 1)
.
La razón por la que su código no se compilará es que pr(5 -1)
no fue reemplazado por el preprocesador, por lo que termina en la fuente como una llamada a una función indefinida.
Tu no eres supuesto tener macros recursivas en C o C++.
El lenguaje relevante del estándar C++, sección 16.3.4 párrafo 2:
Si el nombre de la macro que se reemplaza se encuentra durante este escaneo de la lista de reemplazo (sin incluir el resto de los tokens de preprocesamiento del archivo de origen), no se reemplaza. Además, si algún reemplazo anidado encuentra el nombre de la macro que se reemplaza, no se reemplaza. Estos tokens de preprocesamiento de nombre de macro no reemplazados ya no están disponibles para un reemplazo posterior, incluso si se (re)examinan más tarde en contextos en los que ese token de preprocesamiento de nombre de macro se habría reemplazado de otro modo.
Hay cierto margen de maniobra en este idioma. Con múltiples macros que se invocan entre sí, hay un área gris donde esa redacción no dice exactamente qué se debe hacer. Hay un problema activo contra el estándar de C++ con respecto a este problema del abogado del lenguaje; ver http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#268 .
Ignorando el problema del abogado del lenguaje, todos los proveedores de compiladores entienden la intención:
Las macros recursivas no están permitidas en C o en C++.
Lo más probable es que no pueda ejecutarlo porque no puede compilarlo. Además, si se compilara correctamente, siempre devolvería 1. ¿Quiso decir (n==1)? 1 : n * pr(n-1)
.
Las macros no pueden ser recursivas. De acuerdo con el capítulo 16.3.4.2 (gracias Loki Astari), si la macro actual se encuentra en la lista de reemplazo, se deja como está, por lo tanto, su pr
en la definición no se cambiará:
Si el nombre de la macro que se reemplaza se encuentra durante este escaneo de la lista de reemplazo (sin incluir el resto de los tokens de procesamiento previo del archivo de origen), no se reemplaza. Además, si algún reemplazo anidado encuentra el nombre de la macro que se reemplaza, no se reemplaza. Estos tokens de preprocesamiento de nombre de macro no reemplazados ya no están disponibles para un reemplazo posterior, incluso si se (re)examinan más tarde en contextos en los que ese token de preprocesamiento de nombre de macro se habría reemplazado de otro modo.
Tu llamada:
cout<<"result: "<< pr(5) <<endl;
fue convertido por preprocesador en:
cout<<"result: "<< (5==1)? 1 : pr(5-1) <<endl;
Durante esto, la definición de pr
la macro está ‘perdida’ y el compilador muestra un error como “‘pr’ no se declaró en este ámbito (de hecho)” porque no hay una función nombrada pr
.
No se recomienda el uso de macros en C++. ¿Por qué no simplemente escribes una función?
En este caso, incluso podría escribir una función de plantilla para que se resuelva en tiempo de compilación y se comporte como un valor constante:
template <int n>
int pr() { pr<n-1>(); }
template <>
int pr<1>() { return 1; }
No puede tener macros recursivas en C o C++.
Las macros de C son macros de texto. Si las macros fueran recursivas, SIEMPRE construirías una expresión infinita porque las macros no pueden hacer literalmente nada más que ‘reemplazar esta con que‘
– Cúbico
16 de septiembre de 2012 a las 14:19
@Cubic: En realidad, las macros pueden hacer mucho más. Citación de parámetros, concatenación de texto y reemplazo iterativo de macros definidas posteriormente. Pero no la recursividad.
– Martín York
16 de septiembre de 2012 a las 15:16
No estoy seguro POR QUÉ te gustaría hacer esto. si tiene la intención de realizar un cálculo recursivo en el momento de la compilación, es posible que le interesen las plantillas variádicas (una nueva característica del nuevo estándar C++).
– Alejandro Ah
8 de junio de 2013 a las 22:14
no, pero las plantillas, por otro lado, son Turing complete.stackoverflow.com/questions/189172/c-templates-turing-complete
– Jasén
7 de mayo de 2019 a las 2:26