I'm experimenting to see how far I can abuse the C preprocessor and I have stumbled across an interesting problem.
I have the following macro defines:
#define if(x) if (x)
#define do {
#define elif(x) } else if (x) {
#define else } else {
#define done }
Which should allow me to write:
if (i == 1)
do
...
elif (i == 2)
...
else
...
done
And it works perfectly fine if I only use if
and else
, except the introduction of elif
is problematic because the macro expands as:
} } else { if (x) {
due to the else
being defined.
Is there any way I can get elif
to use 'raw' else
without having it picked up by the preprocessor? I think I need to try nesting multiple defines to trick the preprocessor into pasting the word directly without parsing it but I'm not sure how to achieve this.
Any ideas, or is this not possible in GCC?
Edit:
In essence, this can be boiled down to the following problem:
#define A B
#define B C
For the two given defines A
and B
, how can I get A
to still resolve to the literal word B
and not go through the second define and end up as C
?
I think I managed to solve it. I utilized that:
if(x) {
...
}
is the same as
for(; x ;) {
...
break:
}
What we need from there is to save the result of x
. We cannot reuse it, since x
might be an expression with side effects. So:
int b;
for(; b = (x);) {
break;
}
Now, we can check b
to see if the above for loop was executed or not. A complete if-elif-else pattern done with for loops can look like this:
for(;b = (x);) { // if
...
break;
}
for(; !b ? b=(x==1) : 0;) { // elif
...
break;
}
for(; !b ;) { // else
...
break;
}
With that, we can wrap it up like this, but be aware. However, this will not work well if you do a if(x) break
inside a loop. See below.
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; }
Demo: https://onlinegdb.com/Zq6Y7vm5Q
An alternative approach without break
statements:
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 }
Do note however, that both of these might fail if you have a break
statement in them like this:
for(...) {
if(x)
do
break;
done
}
Because that would expand to:
for(...) {
for(int c=1 ; c && (b = !!(x)); c=0)
{
break;
}
}
Note:
It should be obvious, but if you decide to use this code (don't) then use better names than b
and c
to avoid collisions.
Not quite what you asked for, but you have admitted that you're basically just abusing the preprocessor. :)
But an easy workaround is to use a synonym for else
.
#define if(x) if (x)
#define do {
#define elif(x) } else if (x) {
#define otherwise } else {
#define done }
Demo: https://onlinegdb.com/Cp-gYpOvm
It works with zero, one or multiple instances of elif, and regardless of how many elifs, it works with and without otherwise.