From: Phil Howard
Subject: Re: how to give variable number of args for macro
Newsgroups: comp.lang.c
Message-ID: <Vv0D2.339$v8.10430@newsfeed.slurp.net>
Date: Wed, 03 Mar 1999 01:46:29 GMT

Here's another way that's not in the FAQ. It does depend on the vfprintf function being in your library and on a compiler that optimizes out expressions that cannot be executed. Most modern libraries and compilers do have this. Even if the optimization is absent, the trick will still work although the executeable will have some dead code in it.

I start by defining the symbol "debug" according to the state of the symbol "DEBUG" like so: (this can be in your .h file)

#ifdef DEBUG
#define debug                   _debug_call
#else
#define debug                   1?0:
#endif

Then I define a function called "_debug_call()" which accepts one or more fixed arguments followed by a variable number of arguments. For example:

        int _debug_call( int msglev, const char *msgfmt, ... )

That function will need to include stdarg.h and have a variable defined:

        va_list arg_list;

Then following any code which does things like deciding whether the message should even be printed (I have varying debug levels, for example), I have

        va_start( arg_list, msgfmt );
        vfprintf( stderr, msgfmt , arg_list );
        putc( '\n', stderr );

Now if the symbol DEBUG is not defined, then the symbol debug is define with the string "1?0:". When the pre-processor sees something like

        debug( 1, "begin %s", argv[0] );
It turns it into:
        1?0:(1,"begin %s",argv[0]);
which is now a ?: expression which evaluates the left side of : but not the right side. The right side is merely a parenthesized expression which, if it were evaluated, would discard all comma separated expressions but the last, yielding the last. But the ?: causes it to not be evaluated and a good compiler will recognize that since 1 is a constant, the right side of : cannot ever be evaluated, and not even compile it it. This it becomes a free standing value. If you did choose to assign debug() to something line:
        result = debug( 1, "begin %s", argv[0] );
Then you get:
        result = 1?0:(1,"begin %s",argv[0]);
which has the effect of:
        result = 0;
Now if DEBUG is defined, then it becomes:
        _debug_call(1,"begin %s",argv[0]);
and voila, it is compiled as a function call. You do have to place the definition of "DEBUG" before the pre-processor code that decides how to define "debug". I do this by having the part the decides how to define "debug" in a header file, called debug.h, and I simply define, or not, the symbol "DEBUG" before including debug.h.

Above I have given a simple version. My actual debug package is a bit more complex, having features to manage debug levels, which files to output to, and a separately named duplication called trace (the same thing all over again, but with a new name which I can use for a different group of concepts, and a separate messaging level state).

If anyone wants to include the above trick in an FAQ or publish it or whatever, feel free to do so.

--
 --    *-----------------------------*      Phil Howard KA9WGN       *    --
  --   | Inturnet, Inc.              | Director of Internet Services |   --
   --  | Business Internet Solutions |       eng at intur.net        |  --
    -- *-----------------------------*      phil at intur.net        * --