15

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:

  • This creates a compound literal char array and initializes it by using a string literal.
  • No matter what is passed to the macro, all arguments will be translated to one long string literal.
  • In case the macro list is empty, the string literal will become "", which consists only of a null terminator and therefore has size 1.
  • In all other cases, it will have a size greater than 1.
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 2
    Are you overlooking the fact that [no conforming invocation of a variadic macro has an empty variable argument list](https://port70.net/~nsz/c/c11/n1570.html#6.10.3p4)? There cannot be a conforming way to test for that condition (other than to assume it does not hold) because any code that exhibits it has undefined behavior for that reason. – John Bollinger Mar 29 '19 at 12:18
  • @JohnBollinger Firefox says that the link is unsafe. I read 6.10.3/4 as only applying to macros that don't end with `...`? – Lundin Mar 29 '19 at 12:24
  • GCC returns `1` on `is_empty(\ )`, with friendly `warning: invalid string literal, ignoring final '\'`. I am not entirely sure if this is non-conforming code and/or issue with gcc... – user694733 Mar 29 '19 at 12:42
  • @JohnBollinger, it has variable argument list that consists of one, but empty, token. Not sure where you want to go with this. The proposed macro is certainly legitimate and does what it claims to do. – Jens Gustedt Mar 29 '19 at 12:47
  • @Lundin, it's a link to the same HTML version of draft N1570 that I've been using for years. Evidently Firefox has stopped trusting the CA that issued port70.net's server cert (it recently dropped support for several big CAs). But in any case, the relevant quote is "there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...)." – John Bollinger Mar 29 '19 at 13:09
  • @JensGustedt, there can be a single variable argument consisting of zero preprocessing tokens (`A_MACRO(regular_arg,)`), but that's not the same thing as an empty variable argument list (`A_MACRO(regular_arg)`). – John Bollinger Mar 29 '19 at 13:13
  • "•In all other cases, it will have a size greater than 1." is this true? `is_empty("")` a false positive? – chux - Reinstate Monica Mar 29 '19 at 13:26
  • @JohnBollinger Yes, and I believe that whole paragraph is about "If the identifier-list in the macro definition does not end with an ellipsis...", or am I reading it wrong? – Lundin Mar 29 '19 at 13:37
  • You are reading it wrongly, @Lundin. The paragraph describes both the variadic and non variadic cases. The latter, from which my quotation is drawn, so indicates by starting with "Otherwise", and it is clear that it applies to the variadic case because it explicitly excludes the `...` from the parameter count. – John Bollinger Mar 29 '19 at 15:01
  • 1
    @chux, stringifying a `""` preprocessing token yields a string literal of size 3, containing two double-quote characters. – John Bollinger Mar 29 '19 at 15:03
  • What result do you expect given `#define X` (newline) `#define ONE_ARG(v, ...) (is_empty(__VA_ARGS__))` (newline) `const int test = ONE_ARG(X, X);`? – aschepler Mar 31 '19 at 23:36

2 Answers2

7

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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • 1
    "This is a language constraint, so not only does C not define the behavior of a program containing an invocation of a variadic macro with an empty VA list, but a conforming implementation will issue a diagnostic about it." But it compiles cleanly with latest clang, gcc and icc. https://godbolt.org/z/CBVm9s. Maybe it's a flaw in the standard, shouldn't it say "there shall be as least as many arguments"? Because obviously we are allowed to call `printf("test")`, 1 argument, even though the function is `int printf(fmt, ...)` and variadic macros were meant to mimic variadic functions. – Lundin Mar 29 '19 at 15:07
  • 1
    @Lundin, the rules for variadic functions are different from those for variadic macros. Functions require at least one named parameter and permit empty variadic argument lists. Macros do not require any named parameters and do not allow empty argument lists. – John Bollinger Mar 29 '19 at 15:33
  • 1
    @Lundin, A compiler that does not diagnose an invocation of a variadic macro with an empty variable argument list is *for that reason* non-conforming. I do not find it particularly surprising that various compilers are non-conforming by default, and I also do not find it particularly relevant. I don't know any way to interpret your request for a *valid* way to diagnose an empty VA list other than as a request for a *conforming* one. – John Bollinger Mar 29 '19 at 15:44
  • 1
    I note also that for GCC, at least, if I pass the `-pedantic` argument to ensure that it emits all diagnostics required by the standard, then it indeed does diagnose invocation of a variadic macro with an empty variable argument list (in the unambiguous case, `CALL(foo)` *vs*. `CALL(foo,)`). – John Bollinger Mar 29 '19 at 15:52
  • 1
    "an ambiguity in the language" I'm confused. *Ambiguity* implies there are two interpretations; *standard does not specify* implies we can't pick one. But I only see one interpretation of `is_empty( )` given `#define is_empty(...)`, unambiguously specified by the very paragraph you quote... that being, `is_empty( )` in this case is a macro invocation with one argument. – H Walters Mar 29 '19 at 16:49
  • 1
    @HWalters, before you can *combine* arguments (at least one required) to form the variable arguments, per the quotation, you must *have* arguments in the first place. This is what is ambiguous in the case of a zero-token argument list. If `is_empty` were instead a non-variadic function-like macro taking zero arguments (`#define is_empty()`) then would you interpret an invocation of the form `is_empty()` -- the exact same token sequence -- as (erroneously) having an argument? – John Bollinger Mar 29 '19 at 18:02
  • @Lundin It being an error in the standard doesn’t make the behavior defined. The standard is what the implementers go with, so unless there’s a problem report addressing this specific aspect of the standard – and it’s taken into account in the compiler of your choice (!), you must refrain from writing such code unless your compiler vendor has documented a behavior that suits your needs – but then your program is not portable anymore. – Kuba hasn't forgotten Monica Mar 29 '19 at 19:11
  • I think you are just wrong in your interpretation. A macro with just `...` as argument list that is called with nothing between the `()` is called with one argument, the empty token. So it has indeed one argument more than would have the macro without the `...`. I don't see any ambiguity here and all compilers that I know of interpret the standard that way. And then you also seem to be wrong in your conclusion, if it would be undefined, it would not be a constraint violation to use it that way. It would be up to the implementation to allow it or not. – Jens Gustedt Mar 29 '19 at 22:10
  • Well, @JensGustedt, you're as much entitled to your opinion as I am to mine. But although I well see how the standard affords the interpretation you describe, I truly do not understand how you or anyone can conclude that it is the only viable or plausible interpretation. I'm not even sure how you conclude that the observed behavior of implementations supports that interpretation exclusively, as opposed, say, to implementations just ignoring C's rule that there be at least one variable argument. – John Bollinger Mar 29 '19 at 23:47
  • @JohnBollinger Given `#define Z()`, `Z()` is a macro invocation with 0 arguments. OTOH, given `#define O(x)`, `O()` is a macro invocation with 1 argument (compare def/usage of `q` in 6.10.3.5ex3). Both invocations have zero tokens between the parentheses, but that's insufficient for the count; the argument count per 6.10.3p4 comes from how the macro is defined, and `Z` is defined with no parameters whereas `O` is defined with one. So given `#define V(...)`, `V()` per 6.10.3p4 has one argument (one more than zero), like `O()`. I see no phrase suggesting it has 0 arguments like `Z()`. – H Walters Mar 30 '19 at 04:22
  • Also, you seem to be confused about the preprocessor. `sizeof` is a keyword, and keywords are *tokens* (6.4p1, 6.4.1p2). Tokens are minimal lexical elements during translation phase 7 and 8; phases 3 through 6 use *preprocessing tokens* (6.4p3). Preprocessing directives are deleted at the end of translation phase 4 (5.1.1.2p1). So it's indeed *very* clear that sizeof does not work in a conditional directive; it's three translation phases away from even being a keyword in the first place. – H Walters Mar 30 '19 at 15:32
  • @HWalters, you are arguing that the interpretation of the number of arguments to a macro invocation depends on the macro's definition. This is *not* (clearly) specified by the standard. The standard generally takes considerable care to spell out these kinds of details. The only reason even to consider the somewhat contrived interpretation of a single zero-token argument is an expectation in some quarters -- not explicitly supported by the standard -- that one *should* be able to use an empty argument list with with a variadic macro. – John Bollinger Mar 31 '19 at 12:26
  • @HWalters, some of the same people expect to be able to omit all variable arguments to variadic macros having at least one named argument `#define foo(x, ...)`, and although most compilers will accept that, it is clearly contrary to the standard. This establishes that people's expectations are not a reliable basis for interpreting the standard. – John Bollinger Mar 31 '19 at 12:33
  • @HWalters, as for `sizeof`, it is an *operator*, not a keyword (see section 6.5.3, and compare to 6.4.1). It is explicitly allowed in the grammar for the standard's formal `constant-expression` production, which is how it also defines the expression in a conditional compilation directive. So explain to me in a little more detail, then, why shouldn't `sizeof` be recognized in conditional compilation directives, but other operators, such as `+`, are? – John Bollinger Mar 31 '19 at 12:50
  • @JohnBollinger That `sizeof` is an operator does not mean `sizeof` is not a keyword. Keywords are lexical entities; they're just reserved identifiers. Operators are pieces of expressions. See context of 6.5p1, and 6.10.1p1 both for its context and its applicability to the integer constant expressions supported by conditionals. See especially the footnote: "Because the controlling constant expression is evaluated during translation phase 4, all identifiers either are or are not macro names — there simply are no keywords, enumeration constants, etc." – H Walters Mar 31 '19 at 14:09
  • "This is not (clearly) specified by the standard." To me, it's clear that `Z()` has 0 arguments; that `O()` is allowed; and that, given `O()` is allowed, `O()` has 1 argument. Which of these things do you think the standard doesn't clearly support? – H Walters Mar 31 '19 at 14:46
  • @HWalters, In fact, that `sizeof` is an operator *does* mean that it is not a keyword. And that it is not on the list of keywords (section 6.4.1) confirms it. Moreover, that it has the lexical form of an identifier does not imply that it cannot or should not be interpreted as an operator by the preprocessor, for the preprocessor definitely *does* recognize the `defined` and `_Pragma` operators, both of which have the lexical form of identifiers. – John Bollinger Mar 31 '19 at 18:25
  • @HWalters, If the `O()` macro invocation is allowed then it is because that *follows from* the zero preprocessing tokens of its argument list being interpreted as one argument instead of zero. Using `O()` being allowed as a justification for that interpretation of the argument list makes your logic circular. A natural reading of the standard would lead me to conclude that both `O()` and `Z()` have zero arguments, and that `O()` is therefore disallowed. I see nothing in the standard to contradict that. Nevertheless, some *do* interpret it differently, so it is manifestly ambiguous. – John Bollinger Mar 31 '19 at 18:46
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/190990/discussion-between-h-walters-and-john-bollinger). – H Walters Mar 31 '19 at 18:46
1

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

silmaril
  • 423
  • 3
  • 10