cc-preprocessorc11variadic-macros

Is this a valid way of checking if a variadic macro argument list is empty?


I've been looking for a way to check if a variadic macro argument list is empty. All solutions I find seem to be either quite complex or using non-standard extensions.

I think I've found an easy solution that is both compact and standard:

#define is_empty(...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )

Q: Are there any circumstances where my solution will fail or invoke poorly-defined behavior?

Based on C17 6.10.3.2/2 (the # operator): "The character string literal corresponding to an empty argument is """, I believe that #__VA_ARGS__ is always well-defined.

Explanation of the macro:


Solution

  • Note: this version of this answer is the result of a major rewrite. Some claims have been removed and others significantly modified, so as to focus on and better justify the most important points.

    Variadic macros and their variable arguments

    [Controversial, much disputed position removed. It was more distracting than helpful.]


    The proposed macro

    I think I've found an easy solution that is both compact and standard:

    #define is_empty(...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )
    

    We can sidestep any question of undefinedness by considering this variation:

    #define is_empty(dummy, ...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )
    

    . The same considerations apply to the interpretation of empty vs. non-empty variable arguments here as do in your original version. Specifically,

    Based on C17 6.10.3.2/2 (the # operator): "The character string literal corresponding to an empty argument is """, I believe that #__VA_ARGS__ is always well-defined.

    I agree. Also relevant here is section 6.10.3.1/2: "An identifier __VA_ARGS__ that occurs in the replacement list shall be treated as if it were a parameter [...]."

    Explanation of the macro:

    • This creates a compound literal char array and initializes it by using a string literal.

    Yes.

    • No matter what is passed to the macro, all arguments will be translated to one long string literal.

    Yes. __VA_ARGS__ is treated as a (one) parameter. If there are multiple variable arguments then that can impact the rescan, but the stringification operator has its effect at the point of the macro expansion, before rescanning.

    • In case the macro list is empty, the string literal will become "", which consists only of a null terminator and therefore has size 1.

    Yes.

    • In all other cases, it will have a size greater than 1.

    Yes. This holds even in the case of two zero-token arguments in the variable argument list, is_empty(dummy,,), where #__VA_ARGS__ will expand to ",". It also holds in the case of an argument consisting of an empty string literal, is_empty(dummy, ""), where #__VA_ARGS__ will expand to "\"\"".

    HOWEVER, that still might not serve your purpose. In particular, you cannot use it in a conditional compilation directive. Although sizeof expressions are generally allowed in integer constant expressions, such as form the control expressions of such directives,

    Therefore, if your macro is used as or in the control expression of a conditional compilation directive then it will be evaluated as if the sizeof operator in it were replaced by 0, yielding an invalid expression.