Estoy experimentando para ver hasta dónde puedo abusar del preprocesador C y me he topado con un problema interesante.
Tengo las siguientes definiciones de macro:
#define if(x) if (x) #define do { #define elif(x) } else if (x) { #define else } else { #define done }Lo que debería permitirme escribir:
if (i == 1) do ... elif (i == 2) ... else ... done Y funciona perfectamente bien si solo uso if y else , excepto que la introducción de elif es problemática porque la macro se expande como:
} } else { if (x) { debido a que el else está definido.
¿Hay alguna forma de que elif use 'raw' else sin que el preprocesador lo recoja? Creo que necesito intentar anidar múltiples definiciones para engañar al preprocesador para que pegue la palabra directamente sin analizarla, pero no estoy seguro de cómo lograrlo.
¿Alguna idea, o esto no es posible en GCC?
Editar:
En esencia, esto se puede reducir al siguiente problema:
#define AB #define BC Para las dos definiciones A y B dadas, ¿cómo puedo hacer que A aún se resuelva en la palabra literal B y no pase por la segunda definición y termine como C ?
Creo que logré solucionarlo. Utilicé eso:
if(x) { ... }es lo mismo que
for(; x ;) { ... break: } Lo que necesitamos a partir de ahí es guardar el resultado de x . No podemos reutilizarlo, ya que x podría ser una expresión con efectos secundarios. Asi que:
int b; for(; b = (x);) { break; } Ahora, podemos verificar b para ver si el bucle for anterior se ejecutó o no. Un patrón if-elif-else completo hecho con bucles for puede verse así:
for(;b = (x);) { // if ... break; } for(; !b ? b=(x==1) : 0;) { // elif ... break; } for(; !b ;) { // else ... break; } Con eso, podemos terminarlo así, pero ten cuidado. Sin embargo, esto no funcionará bien si hace un if(x) break dentro de un ciclo. Vea abajo.
int b; // Store truth value of last if or elif #define if(x) for(;b = !!(x);) #define do { #define elif(x) break; } for(; !b ? b=!!(x) : 0;) { #define else break; } for(;!b;) { #define done break; }Demostración: https://onlinegdb.com/Zq6Y7vm5Q
Un enfoque alternativo sin declaraciones de break :
int b; // Store truth value of last if or elif #define if(x) for(int c=1 ; c && (b = !!(x)); c=0) #define do { #define elif(x) } for(int c=1; c && (!b ? b=!!(x) : 0); c=0) { #define else } for(int c=1; c && !b; c=0) { #define done } Sin embargo, tenga en cuenta que ambos pueden fallar si tiene una declaración de break como esta:
for(...) { if(x) do break; done }Porque eso se expandiría a:
for(...) { for(int c=1 ; c && (b = !!(x)); c=0) { break; } }Nota:
Debería ser obvio, pero si decide usar este código (no lo haga), use mejores nombres que b y c para evitar colisiones.
No es exactamente lo que pediste, pero admitiste que básicamente estás abusando del preprocesador. :)
Pero una solución fácil es usar un sinónimo de else .
#define if(x) if (x) #define do { #define elif(x) } else if (x) { #define otherwise } else { #define done }Demostración: https://onlinegdb.com/Cp-gYpOvm
Funciona con cero, una o múltiples instancias de elif, e independientemente de cuántos elifs, funciona con y sin otros.
Sobre la base de las ideas de klutt , aquí hay una alternativa sin la necesidad de una variable adicional, pero limitada a una sola cláusula elif :
#define if(x) switch (!!(x)) #define do { case 1: { #define elif(x) } break; default: switch (!!(x)) { case 1: #define else } break; default: { #define done }} Además de la deficiencia de elif , esta solución no combina bien con bucles e interruptores escritos por el usuario. Estos casos serán mal manejados:
for(...) { if(...) break; } switch (x) { case 0: if (...) break; else { y = 42; break; } ... }Para su propósito, usar pseudo-palabras clave en mayúsculas parece un mejor enfoque (y deliberadamente feo).
#define IF(x) if (x) #define DO { #define ELIF(x) } else if (x) { #define ELSE } else { #define DONE }