tutorial sintaxis recursividad programacion indirecta funcion definicion arreglos c++ c macros c-preprocessor

c++ - sintaxis - ¿Podemos tener macros recursivas?



recursividad indirecta c++ (5)

Quiero saber si podemos tener macros recursivas en C / C ++? En caso afirmativo, proporcione un ejemplo de muestra.

Segundo: ¿por qué no puedo ejecutar el código siguiente? ¿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 provisionales. Cuando el preprocesador escanea y expande pr(5) :

pr(5) ^

crea un contexto de desactivación, de modo que cuando ve pr nuevamente:

((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 vuelva azul al usar expresiones diferidas y alguna indirección:

# 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))

Entonces ahora se expandirá así:

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

Lo cual es perfecto, porque pr nunca fue pintado de azul. Solo tenemos que aplicar otro escaneo para expandirlo 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 expandirlo 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 exploraciones. 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)

Poniéndolo todo junto podemos crear una macro repetida:

#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 ++.


Lo más probable es que no puedas ejecutarlo porque no puedes compilarlo. Además, si se compilara correctamente, siempre regresaría 1. ¿Querías decir (n==1)? 1 : n * pr(n-1) (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 cambiará:

Si se encuentra el nombre de la macro que se reemplaza durante este escaneo de la lista de reemplazo (sin incluir el resto de los tokens de preprocesamiento del archivo fuente), no se reemplaza. Además, si alguno de los reemplazos anidados encuentra el nombre de la macro que se reemplaza, no se reemplaza. Estos tokens de preprocesamiento de nombres de macro no reemplazados ya no están disponibles para un reemplazo adicional, incluso si luego se (re) examinan en contextos en los que ese token de preprocesamiento de nombre de macro hubiera sido reemplazado de otra manera.

Tu llamada:

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

fue convertido por el preprocesador en:

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

Durante esto, la definición de pr macro se ''pierde'', y el compilador muestra un error como "''pr'' no se declaró en este alcance (hecho)" porque no hay función llamada pr .

El uso de macros no se recomienda en C ++. ¿Por qué no solo 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 ++.


No se supone que tengas macros recursivas en C o C ++.

El lenguaje relevante del estándar C ++, sección 16.3.4 párrafo 2:

Si se encuentra el nombre de la macro que se reemplaza durante este escaneo de la lista de reemplazo (sin incluir el resto de los tokens de preprocesamiento del archivo fuente), no se reemplaza. Además, si alguno de los reemplazos anidados encuentra el nombre de la macro que se reemplaza, no se reemplaza. Estos tokens de preprocesamiento de nombres de macro no reemplazados ya no están disponibles para un reemplazo adicional, incluso si luego se (re) examinan en contextos en los que ese token de preprocesamiento de nombre de macro hubiera sido reemplazado de otra manera.

Hay un margen de maniobra en este idioma. Con múltiples macros que se invocan entre sí, hay un área gris donde esa fraseología no dice exactamente qué se debe hacer. Existe un problema activo en relación con el estándar de C ++ con respecto a este problema de abogado de idioma; ver http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#268 .

Ignorando el problema del abogado de idioma, cada vendedor del compilador entiende la intención:

Las macros recursivas no están permitidas en C o en C ++.


Su compilador probablemente proporciona 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 para recordar es que todo lo que hace el pre-procesador es reemplazar ciegamente una cadena de texto por otra, en realidad no evalúa expresiones como (x == 1) .

La razón por la cual 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 no definida.