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

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,

  • lexically, as a preprocessing token, sizeof is categorized as an identifier (there is no distinction between keywords and identifiers for preprocessing tokens), and
  • according to paragraph 6.10.1/4 of the standard, when processing the control expression of a conditional compilation directive,

    After all replacements due to macro expansion and the defined unary operator have been performed, all remaining identifiers (including those lexically identical to keywords) are replaced with the pp-number 0

    (emphasis added).

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.


Personnally i don't like mixing macro/preprocessor-level evaluation and compilation-level test.

There seem to be no standard way to do it at the macro level, but hacks exists here: C++ preprocessor __VA_ARGS__ number of arguments