0

I have these lines

#define LOG(fp, fmt, ...) fprintf(fp, "%s:%d: "fmt, __FILE__, __LINE__, ## __VA_ARGS__) 
#define OUT(fmt, ...) LOG(stdout, fmt, __VA_ARGS__) 

Compiler shows OUT to be erroneous. How do I call LOG from within OUT?

Error messages:

  1. expected expression before ‘)’ token in LOG
  2. expansion error when I use it as OUT("Hello world");
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
pmahlovic
  • 3
  • 1
  • 3
    What compiler are you using, and what error message is it giving for what use of `OUT`? – Wintermute Feb 13 '15 at 17:35
  • Error messages 1)expected expression before ‘)’ token in LOG and 2) expansion error when I use it as OUT("Hello world"); – pmahlovic Feb 13 '15 at 17:36
  • @alk I just got it from the book and it works – pmahlovic Feb 13 '15 at 17:40
  • 2
    Err .. "*it works*"? Your question states it doesn't. – alk Feb 13 '15 at 17:40
  • @alk LOG itself works without problems. Only when I call it from OUT, that's when the errors appear – pmahlovic Feb 13 '15 at 17:43
  • Replace `OUT("Hello world")` with `OUT("%s", "Hello world")`. – alk Feb 13 '15 at 17:44
  • Note the section 'Single Argument C99 Variant' in the [C `#define` macro for debug printing](http://stackoverflow.com/questions/1644868/c-define-macro-for-debug-printing/1644898#1644898) duplicate. IMO, the other possible duplicate has very limited relevance; it deals with something this question was not attempting to do. – Jonathan Leffler Feb 13 '15 at 18:28

2 Answers2

3

Short explanation because I take from the comments that this was cargo-culted into the code: The ## in ##__VA_ARGS__ is a non-standard extension of the preprocessor (originally from gcc, now also supported by clang). Its effect is that if a variadic macro is called with no variadic parameters, a superfluous preceding comma is removed. That is to say, where

#define FOO(bar, ...) foo(bar, __VA_ARGS__)

cannot be called with one argument because it would expand to foo(argument,),

#define FOO(bar, ...) foo(bar, ##__VA_ARGS__)

can because the comma is silently removed and the expansion is foo(argument).

Therefore, the solution to your problem is to use

//                                     vv--- these are important here
#define OUT(fmt, ...) LOG(stdout, fmt, ##__VA_ARGS__) 

Otherwise in an expansion of OUT with only one argument, the __VA_ARGS__ in LOG will not be empty because it is expanded from

LOG(stdout, "Hello, world.",)

instead of

LOG(stdout, "Hello, world.")

...and the ## before __VA_ARGS__ in LOG will then have no effect. You get the compiler message because there's an extra comma in the expansion of LOG (the fprintf call ends with ,)).

Link to specifics.

Wintermute
  • 42,983
  • 5
  • 77
  • 80
  • Cool and so simple! I have just tested it! – pmahlovic Feb 13 '15 at 17:52
  • As far as I see my gcc's warnings this approach is not C99 compliant. Using `-pedantic` I get `warning: ISO C99 requires rest arguments to be used [enabled by default]`. – alk Feb 13 '15 at 17:56
  • @alk that is correct. It was originally implemented by gcc, and I just tried with clang 3.5, which also supports it, but I would be surprised if MSVC didn't choke on it. However, MSVC suppresses the trailing comma without `##`, so while confusion is thoroughly achieved, it is possible to hack one's way around the problem with the most common compilers. – Wintermute Feb 13 '15 at 18:00
0

The ... in OUT(fmt, ..) implies the need to pass at least one more argument then just fmt. Like for example:

OUT("%s", "Hello World");

If doing so the ## is useless and the macros should look like:

#define LOG(fp, fmt, ...) fprintf((fp), "%s:%d: "fmt, __FILE__, __LINE__, __VA_ARGS__) 
#define OUT(fmt, ...) LOG(stdout, fmt, __VA_ARGS__) 
alk
  • 69,737
  • 10
  • 105
  • 255