C99's variadic macros.

Discussion in 'C Programming' started by João Jerónimo, Dec 7, 2008.

  1. 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
     
    João Jerónimo, Dec 7, 2008
    #1
    1. Advertising

  2. 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
     
    João Jerónimo, Dec 7, 2008
    #2
    1. Advertising

  3. João Jerónimo <> 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.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.
     
    Nate Eldredge, Dec 7, 2008
    #3
  4. João Jerónimo

    Ben Pfaff Guest

    João Jerónimo <> 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[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.\
    \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\
    );}return 0;}
     
    Ben Pfaff, Dec 8, 2008
    #4
  5. João Jerónimo <> 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.
     
    Ben Bacarisse, Dec 8, 2008
    #5
  6. João Jerónimo

    SozzlyJoe Guest

    On 7 Dec, 18:28, João Jerónimo <> 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/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.
     
    SozzlyJoe, Dec 9, 2008
    #6
  7. On 7 déc, 20:14, Nate Eldredge <> wrote:
    > João Jerónimo <> 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.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.
     
    Laurent Deniau, Dec 9, 2008
    #7
  8. João Jerónimo <> 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? :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__)

    --
    Ben.
     
    Ben Bacarisse, Dec 9, 2008
    #8
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Colin Walters
    Replies:
    2
    Views:
    544
    Ben Pfaff
    Feb 13, 2004
  2. Ross A. Finlayson
    Replies:
    19
    Views:
    652
    Keith Thompson
    Mar 10, 2005
  3. Replies:
    2
    Views:
    368
    Dave Thompson
    Feb 27, 2006
  4. Thomas Carter

    Variadic macros

    Thomas Carter, Feb 23, 2006, in forum: C Programming
    Replies:
    3
    Views:
    430
    S.Tobias
    Feb 24, 2006
  5. Replies:
    5
    Views:
    385
Loading...

Share This Page