Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > C99's variadic macros.

Reply
Thread Tools

C99's variadic macros.

 
 
João Jerónimo
Guest
Posts: n/a
 
      12-07-2008
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
 
Reply With Quote
 
 
 
 
João Jerónimo
Guest
Posts: n/a
 
      12-07-2008
João Jerónimo wrote:
> 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
 
Reply With Quote
 
 
 
 
Nate Eldredge
Guest
Posts: n/a
 
      12-07-2008
Joo Jernimo <(E-Mail Removed)> writes:

> 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....ariadic-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.
 
Reply With Quote
 
Ben Pfaff
Guest
Posts: n/a
 
      12-08-2008
Joo Jernimo <(E-Mail Removed)> writes:

> 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.
--
int main(void){char p[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv wxyz.\
\n",*q="kl BIcNBFr.NKEzjwCIxNJC";int i=sizeof p/2;char *strchr();int putchar(\
);while(*q){i+=strchr(p,*q++)-p;if(i>=(int)sizeof p)i-=sizeof p-1;putchar(p[i]\
);}return 0;}
 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      12-08-2008
João Jerónimo <(E-Mail Removed)> writes:

> 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__)

--
Ben.
 
Reply With Quote
 
SozzlyJoe
Guest
Posts: n/a
 
      12-09-2008
On 7 Dec, 18:28, Joo Jernimo <(E-Mail Removed)> wrote:
> 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/DOCUMENTA...ic-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.
 
Reply With Quote
 
Laurent Deniau
Guest
Posts: n/a
 
      12-09-2008
On 7 dc, 20:14, Nate Eldredge <(E-Mail Removed)> wrote:
> Joo Jernimo <(E-Mail Removed)> writes:
> > 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__)


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 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....cros.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_P P_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.
 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      12-09-2008
João Jerónimo <(E-Mail Removed)> writes:

> SozzlyJoe wrote:
>
>> Hope that helps.

>
> 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?
>
> 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__)

--
Ben.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Call again a variadic function (... variable number of arguments)with same arguments that its variadic wrapper moreau.steve@gmail.com C Programming 3 12-31-2008 07:13 AM
variadic function calling variadic function goldfita@signalsguru.net C Programming 5 05-03-2006 05:23 PM
Using variadic functions within variadic functions pinkfloydhomer@gmail.com C Programming 2 02-27-2006 05:47 AM
Variadic functions calling variadic functions with the argument list, HLL bit shifts on LE processors Ross A. Finlayson C Programming 19 03-10-2005 03:57 AM
is casting a variadic function to a non-variadic one legal? Colin Walters C Programming 2 02-13-2004 10:55 PM



Advertisments