1

I am trying to write a function which takes variadic parameters. It has the following prototype:

void foo(const char *name, const char *file, uint32_t line, const char *fmt, ...);

and I call it with the following macro:

#define FOO(name, ...) \
    foo(name, __FILE__, __LINE__, __VA_ARGS__);

From what I understand the following will be valid:

FOO("Example, "Hello %s", "Stack Overflow");

But will the following result in undefined behavior in a standards compliant c99 compiler?

FOO("Example", "Hello Stack Overflow");

My worry is that because the foo is expecting both *fmt as well as ... that a trailing , will be added when there are only two arguments passed to the macro.

Can anyone tell me if the above is valid c99?

EDIT: When I run this with gcc and std=c99 it works, but I am worried that there is silent UB

Thank you!

Chris Frank
  • 4,124
  • 4
  • 30
  • 42

2 Answers2

2

As a side node also related to variadic arguments, C mandates that the variadic part of the macro must be supplied at least one argument (which is the case in the original question).

From ISO/IEC 9899:TC2 §6.10.3:4:

If the identifier-list in the macro definition does not end with an ellipsis, the number of arguments (including those arguments consisting of no preprocessing tokens) in an invocation of a function-like macro shall equal the number of parameters in the macro definition. Otherwise, there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...).

Also there is a draft addressing this very problem: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2034.htm

Fortunately, most modern compilers either suppress the trailing comma or provide some sort of work around. However, this is compiler specific behaviour.

In the original post the variadic part of the FOO macro is given the string literal "Hello Stack Overflow". Upon expansion of the macro the foo function might be called as follows:

foo("Example", "someFile.c", 42 "Hello Stack Overflow");

This is fine, as long as the foo function by some means is aware that there are no more arguments to expect after the last fixed argument.

Kamajii
  • 1,826
  • 9
  • 21
  • So the expanded function-call with only 2 macro arguments results in 4 arguments being passed to `foo`. And `foo` has 4 arguments in it's prototype excluding the `...`. Am I understanding that this results in correct behavior? – Chris Frank Jun 18 '18 at 19:15
2

Per 6.10.3.1 Argument substitution, paragraph 2 of the C standard:

An identifier __VA_ARGS__ that occurs in the replacement list shall be treated as if it were a parameter, and the variable arguments shall form the preprocessing tokens used to replace it.

Thus, __VA_ARGS__ will be replaced with all the tokens passed as arguments to the macro.

So, in your case, given the macro:

#define FOO(name, ...) \
    foo(name, __FILE__, __LINE__, __VA_ARGS__);

called with

FOO("Example", "Hello Stack Overflow");

and the function declaration

void foo(const char *name, const char *file, uint32_t line, const char *fmt, ...);

results in something like

 foo( "Example", "asdf.c", 1234, "Hello Stack Overflow" );

and no undefined behavior in the call to foo. (What happens inside foo() might still be UB.)

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
  • "What happens inside foo() might still be UB." - Haha yes, but this is for another day. Thank you for your answer! – Chris Frank Jun 18 '18 at 19:08
  • 1
    I miscounted arguments... All is ok in here. UB will, however, arise if **foo** tries to access anything variadic as stated above. – Kamajii Jun 18 '18 at 19:17
  • @Kamajii - Am I able to call the va_* family of functions on those variadic parameters? Or would that produce UB? – Chris Frank Jun 18 '18 at 19:20
  • 2
    It would be legal to call `va_start` and `va_end`, which is pretty much what e.g., `printf()` would do with a format string that does not contain placeholders. But you aren't allowed to extract a thing using `va_arg`, obviously. – Kamajii Jun 18 '18 at 19:24
  • @Kamajii Ok perfect. Thanks again to both of you for your time! I appreciate it! – Chris Frank Jun 18 '18 at 19:25