Search code examples
cmacrosmultiline

Multiline macro in c (#if-#endif)


my code:

#define DEBUG 3
#define TEST(...) 
    #if (DEBUG == 3) \
        printf("%s: %s\n", __FILE__, __VA_ARGS__); 
    #endif 

int main(void) {

    TEST("TEST")

    return 0;
}

Error: missing the binary operator before token 'printf'.

I don't understand what's the problem


Solution

  • You're trying to put the #if inside the macro, but C doesn't permit that kind of thing. The reason it's failing is the printf inside the #if is unexpected. This is a common request, but it's just not allowed.

    However, you can accomplish the same thing by changing the way you do the test:

    #include <stdio.h>
    
    #define DEBUG 3
    
    #if (DEBUG == 3)
    # define TEST(...) printf("%s: %s\n", __FILE__, __VA_ARGS__);
    #else
    # define TEST(...)
    #endif
    
    int main(void) {
    
       TEST("TEST")
    
       return 0;
    }
    

    EDIT: Though test-type macros like this are common, this is not a great way to do it because it can lead to unhappy surprises in debug/nondebug situations.

    What happens in this case?

       if (something)
           TEST("blah")
       else
           TEST("no")
    

    This works as you expect in debug mode, but won't even compile in production because it devolves to if (something) else - it doesn't even have a closing semicolon. It's easy to find other examples that are much more devious.

    Hiding a semicolon inside a macro like this is usually asking for trouble, so the better approach is to make them function-like where you have to provide the semicolon yourself, and the most obvious way to do it is:

    #  #define TEST(...) printf("%s: %s\n", __FILE__, __VA_ARGS__) // no semicolon
    
    ...
        if (something)
            TEST("blah");
        else
            TEST("no");
    

    This is better and leads to less surprises, but it's still a little troublesome in that it can leave a dangling semicolon lying around, which some compilers object to.

    A fix for this can be:

    #if (DEBUG == 3)
    # define TEST(...) printf("%s: %s\n", __FILE__, __VA_ARGS__)
    #else
    # define TEST(...) ((void)0)  // dummy statement
    #endif
    

    which at least quiets the compiler. There are other approaches here too.