cmacrospreprocessordefine-syntax

Disallow C preprocessor from using a macro within another


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 ?


Solution

  • Update

    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.

    Old workaround

    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.