C99's variadic macros.

  • Thread starter João Jerónimo
  • Start date
J

João Jerónimo

Hi,

I want to wrap around printf using a C99 variadic macro. However, I don't
know how to get rid of the last comma of the printf call. For example:

#define PRINT_DBG_MESS(FORMAT, ...) fprintf(stderr, "DEBUG: %s:%d: "
FORMAT, __FILE__, __LINE__, __VA_ARGS__)

This is all right if I call the macro with at least one optional argument.
However, if the macro has no argument, it's a syntax error, because the
call to fprintf is left with a comma just before the parentecis.

PRINT_DBG_MESS("I'm %d!\n", 19);
// Expands to:
fprintf(stderr, "DEBUG: %s:%d: " "I'm %d!\n" , "pervert.c", 69, 19);
// Which is perfectly alright.
// However:
PRINT_DBG_MESS("Go to the hell!\n");
// Expands to:
fprintf(stderr, "DEBUG: %s:%d: " "Go to the hell!\n" , "silly.c", 42, );
// Which is toxic...

I solved this with a dirty hack. The story began be defining the macro as:

#define PRINT_DBG_MESS(FORMAT, ...) fprintf(stderr, "DEBUG: %s:%d: "
FORMAT, __FILE__, __LINE__, __VA_ARGS__+0)

Now, the later call got expanded to:

fprintf(stderr, "DEBUG: %s:%d: " "Go to the hell!\n" , "silly.c", 42, +0);

Which is syntactically correct. However, the compiler is smart, and as such
it complains about fprintf having more arguments than it was supposed to
have. So, I defing an alternative wrapper function that would call vfprintf
1:1. I mean:

static void PRINT_DBG_MESS_HELPER(char *format, ...) {
va_list list_of_varargs;
va_start(list_of_varargs, format);
vfprintf(stderr, format, list_of_varargs);
va_end(list_of_varargs);
}

#define PRINT_DBG_MESS(FORMAT, ...) PRINT_DBG_MESS_HELPER( "DEBUG: %s:%d:
" FORMAT, __FILE__, __LINE__, __VA_ARGS__+0)

And now it worked. But it's a dirty hack, and I don't like it! Especially
because ten minutes ago I fed by mistake the macro with a wrong argument
list and the compiled ignored it silently...

PS: I DO want to have the filename and the line number printed.

Thanks in advance,
JJ
 
J

João Jerónimo

João Jerónimo said:
And now it worked. But it's a dirty hack, and I don't like it! Especially
because ten minutes ago I fed by mistake the macro with a wrong argument
list and the compiled ignored it silently...

Oh! I forgot to ask the question...

What's the correct way t wrap around a variadic function using a variadic
macro in C99?

JJ
 
N

Nate Eldredge

João Jerónimo said:
Hi,

I want to wrap around printf using a C99 variadic macro. However, I don't
know how to get rid of the last comma of the printf call. For example:

#define PRINT_DBG_MESS(FORMAT, ...) fprintf(stderr, "DEBUG: %s:%d: "
FORMAT, __FILE__, __LINE__, __VA_ARGS__)

This is all right if I call the macro with at least one optional argument.
However, if the macro has no argument, it's a syntax error, because the
call to fprintf is left with a comma just before the parentecis.

PRINT_DBG_MESS("I'm %d!\n", 19);
// Expands to:
fprintf(stderr, "DEBUG: %s:%d: " "I'm %d!\n" , "pervert.c", 69, 19);
// Which is perfectly alright.
// However:
PRINT_DBG_MESS("Go to the hell!\n");
// Expands to:
fprintf(stderr, "DEBUG: %s:%d: " "Go to the hell!\n" , "silly.c", 42, );
// Which is toxic...

This page:

http://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Variadic-Macros.html#Variadic-Macros

suggests that this can't be done with standard C99, but GCC provides an
extension to make it possible.
I solved this with a dirty hack. The story began be defining the macro as:

#define PRINT_DBG_MESS(FORMAT, ...) fprintf(stderr, "DEBUG: %s:%d: "
FORMAT, __FILE__, __LINE__, __VA_ARGS__+0)

Now, the later call got expanded to:

fprintf(stderr, "DEBUG: %s:%d: " "Go to the hell!\n" , "silly.c", 42, +0);

That's marvelous, and awful.

It's not quite perfect. For example, consider

void *malloc_wrapper(size_t n) {
void *p = malloc(n);
debug("malloc returned %p\n", p);
return p;
}

Since p is of type `void *', `p+0' is illegal in standard C.

A struct argument would also cause a problem, but I suspect you won't be
passing struct arguments to fprintf. There could be other cases that I
haven't thought of, too.
Which is syntactically correct. However, the compiler is smart, and as such
it complains about fprintf having more arguments than it was supposed to
have. So, I defing an alternative wrapper function that would call vfprintf
1:1. I mean:

static void PRINT_DBG_MESS_HELPER(char *format, ...) {
va_list list_of_varargs;
va_start(list_of_varargs, format);
vfprintf(stderr, format, list_of_varargs);
va_end(list_of_varargs);
}

#define PRINT_DBG_MESS(FORMAT, ...) PRINT_DBG_MESS_HELPER( "DEBUG: %s:%d:
" FORMAT, __FILE__, __LINE__, __VA_ARGS__+0)

Seems you could get the same effect by just disabling the compiler's
warning. In GCC, -Wno-format accomplishes this.
And now it worked. But it's a dirty hack, and I don't like it! Especially
because ten minutes ago I fed by mistake the macro with a wrong argument
list and the compiled ignored it silently...

I suppose on top of your terrible hack, you could do

#ifdef CHECK_FOR_WARNINGS_ONLY
#define PRINT_DBG_MESS printf
#else
#define PRINT_DBG_MESS(FORMAT, ...) fprintf(stderr, "DEBUG: %s:%d: " FORMAT, __FILE__, __LINE__, __VA_ARGS__+0)
#endif

and compile with -DCHECK_FOR_WARNINGS_ONLY to see if there are any
warnings, but recompile without it to actually run the code.
 
B

Ben Pfaff

João Jerónimo said:
I want to wrap around printf using a C99 variadic macro. However, I don't
know how to get rid of the last comma of the printf call. For example:

#define PRINT_DBG_MESS(FORMAT, ...) fprintf(stderr, "DEBUG: %s:%d: "
FORMAT, __FILE__, __LINE__, __VA_ARGS__)

If you didn't want to insert the __FILE__ and __LINE__, I'd do it
this way:
#define PRINT_DBG_MESS(...) fprintf(stderr, __VA_ARGS__)

One way to really do it:
#define PRINT_DBG_MESS(...) \
do { \
fprintf(stderr, "DEBUG: %s: %d", __FILE__, __LINE__); \
fprintf(stderr, __VA_ARGS__); \
} while (0)

Another way:
#define PRINT_DBG_MESS(...) do_print_dbg_mess(__FILE__, __LINE__, \
__VA_ARGS__)
and then write an appropriate do_print_dbg_mess() function.
 
B

Ben Bacarisse

João Jerónimo said:
I want to wrap around printf using a C99 variadic macro. However, I don't
know how to get rid of the last comma of the printf call. For example:

#define PRINT_DBG_MESS(FORMAT, ...) fprintf(stderr, "DEBUG: %s:%d: "
FORMAT, __FILE__, __LINE__, __VA_ARGS__)

This is all right if I call the macro with at least one optional argument.
However, if the macro has no argument, it's a syntax error, because the
call to fprintf is left with a comma just before the parentecis.

PRINT_DBG_MESS("I'm %d!\n", 19);
// Expands to:
fprintf(stderr, "DEBUG: %s:%d: " "I'm %d!\n" , "pervert.c", 69, 19);
// Which is perfectly alright.
// However:
PRINT_DBG_MESS("Go to the hell!\n");
// Expands to:
fprintf(stderr, "DEBUG: %s:%d: " "Go to the hell!\n" , "silly.c", 42, );
// Which is toxic...

You can do it like this:

#define PRINT_DBG_MESS(...) PRINT_DBG_EXTRA(__VA_ARGS__, "")

#define PRINT_DBG_EXTRA(FORMAT, ...) \
fprintf(stderr, "DEBUG: %s:%d: " FORMAT "%s", __FILE__, __LINE__, __VA_ARGS__)
 
S

SozzlyJoe

Hi,

I want to wrap around printf using a C99 variadic macro. However, I don't
know how to get rid of the last comma of the printf call. For example:

#define PRINT_DBG_MESS(FORMAT, ...)   fprintf(stderr, "DEBUG: %s:%d: "
FORMAT, __FILE__, __LINE__, __VA_ARGS__)

This is all right if I call the macro with at least one optional argument..
However, if the macro has no argument, it's a syntax error, because the
call to fprintf is left with a comma just before the parentecis.

PRINT_DBG_MESS("I'm %d!\n", 19);
// Expands to:
fprintf(stderr, "DEBUG: %s:%d: " "I'm %d!\n" , "pervert.c", 69, 19);
// Which is perfectly alright.
// However:
PRINT_DBG_MESS("Go to the hell!\n");
// Expands to:
fprintf(stderr, "DEBUG: %s:%d: " "Go to the hell!\n" , "silly.c", 42, );
// Which is toxic...

I solved this with a dirty hack. The story began be defining the macro as:

#define PRINT_DBG_MESS(FORMAT, ...)   fprintf(stderr, "DEBUG: %s:%d: "
FORMAT, __FILE__, __LINE__, __VA_ARGS__+0)

Now, the later call got expanded to:

fprintf(stderr, "DEBUG: %s:%d: " "Go to the hell!\n" , "silly.c", 42, +0);

Which is syntactically correct. However, the compiler is smart, and as such
it complains about fprintf having more arguments than it was supposed to
have. So, I defing an alternative wrapper function that would call vfprintf
1:1. I mean:

static void PRINT_DBG_MESS_HELPER(char *format, ...) {
   va_list list_of_varargs;
   va_start(list_of_varargs, format);
   vfprintf(stderr, format, list_of_varargs);
   va_end(list_of_varargs);

}

#define PRINT_DBG_MESS(FORMAT, ...)   PRINT_DBG_MESS_HELPER( "DEBUG: %s:%d:
" FORMAT, __FILE__, __LINE__, __VA_ARGS__+0)

And now it worked. But it's a dirty hack, and I don't like it! Especially
because ten minutes ago I fed by mistake the macro with a wrong argument
list and the compiled ignored it silently...

PS: I DO want to have the filename and the line number printed.

Thanks in advance,
JJ

Are you using gcc? If so, you can use the ## operator to concatenate
your two arguments together, deleting the comma if the second
(variadic arg )is empty. Unfortunately, AFAIR it only works with
string arguments so you will have to re-arrange your macro to have
format and VA_ARGS together.

This page expresses it well:
http://developer.apple.com/DOCUMENTATION/DeveloperTools/gcc-4.0.1/cpp/Variadic-Macros.html

quote:
Second, the `##' token paste operator has a special meaning when
placed between a comma and a variable argument. If you write

#define eprintf(format, ...) fprintf (stderr, format,
##__VA_ARGS__)

and the variable argument is left out when the eprintf macro is used,
then the comma before the `##' will be deleted. This does not happen
if you pass an empty argument, nor does it happen if the token
preceding `##' is anything other than a comma.

Hope that helps.
 
L

Laurent Deniau

why not simply wrap the function into another one?

extern int myfprintf(FILE*, const char *tag, const char *file, int
line, const char *format, ...);

#define PRINT_DBG_MESS(...) \
myfprintf(stderr, "DEBUG", __FILE__, __LINE__, __VA_ARGS__)
This page:

http://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Variadic-Macros.html#Vari...

suggests that this can't be done with standard C99, but GCC provides an
extension to make it possible.

This is just wrong. Once you have a small library working at the level
of the preprocessor, you can solve this problem in a couple of lines.
The following tested example uses the small cpp lib of the C Object
System (on sourceforge) (see include/cos/cpp)

#if !WRAP_FPRINTF
#define myfprintf(...) fprintf(__VA_ARGS__)
else
#define myfprintf(...) \
fprintf(stderr, "DEBUG: %s:%d:" \
COS_PP_SEQ( \
COS_PP_IF(COS_PP_2ARGS(__VA_ARGS__)) \
((COS_PP_ARG1(__VA_ARGS__),__FILE__,__LINE__,COS_PP_REST
(__VA_ARGS__)), \
(__VA_ARGS__ ,__FILE__,__LINE__))))
#endif

myfprintf("to see\n");

-> fprintf(stderr, "DEBUG: %s:%d:" "to see\n", "the_file", the_line);

myfprintf("to see with arg %d\n", 10);

-> fprintf(stderr, "DEBUG: %s:%d:" "to see with arg %d\n", "the_file",
the_line, 10);

no gcc black magic in there, only c99 code. This a very small example
of what cpp can do...

a+, ld.
 
B

Ben Bacarisse

João Jerónimo said:
Thank you. I'll try this solution.

Meanwhile, I've drafted a candidate solution, but it doesn't work perfectly
for some obscure reason:
------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>

#define INV_COMMAS "
#define PRINT_DBG_MESS(...) fprintf(stderr, "DEBUG: " __FILE__ ":"
INV_COMMAS __LINE__ INV_COMMAS ": " __VA_ARGS__)

You should try to prevent line-wrap where it matters or to break the
line yourself using \.
int main()
{
PRINT_DBG_MESS ("Coisas!\n");
return 0;
}
------------------------------------------------------------

Am I ready for OCCC? :p

Unfortunately, gcc's veredict is:
$ gcc -Wall thing.c -o thing
thing.c: In function ‘main’:
thing.c:10: error: missing terminating " character
thing.c:10: error: expected ‘)’ before numeric constant
thing.c:10: error: missing terminating " character

However, strangely enought, the following actually works!
$ cpp -Wall thing.c | gcc -x c -Wall - -o thing

Stuffing text though a compiler by defeating its attempts to detect
errors does not match my definition of "works"!
And the program runs fine. Why the hell is gcc issuing those errors?

gcc is correct. It is telling you your program is not C. " on its own
s not a valid preprocessing-token -- a list of which is required after
the identifier in an object-like macro definition.

You can do it like this:

#define STR(s) #s
#define XSTR(s) STR(s)
#define PRINT_DBG_MESS(...) fprintf(stderr, "DEBUG: " __FILE__ ":" \
XSTR(__LINE__) ": " __VA_ARGS__)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top