[comp.lang.c] Variable length arg lists for macros

peter@thirdi.UUCP (Peter Rowell) (09/02/88)

If the reason for variable arguments is to pass them to a routine
that takes varargs, the following horrible hack seems not only to
work but to be portable!

#define d_printf(level, stuff) ((level>=debug_level)? printf stuff :level=level)
                                   Note: NO parentheses --> ^     ^

This is then invoked by:

    d_printf(8, ("foo overflow: %d > %d", some_foo, max_foo));
		^ <----- Note the parentheses -----------> ^

By placing parentheses around the args for printf, they become 1 arg
from the point of view of the macro expander.  The reason for the ?: usage
is to allow this "if" anywhere a single expression could be without
screwing up things like:

    if (cond) d_printf(x,("bummer")); else somehthingelse;


----------------------------------------------------------------------
Peter Rowell				(You will need a bigger rock.)
Third Eye Software, Inc.		(415) 321-0967
Menlo Park, CA  94025			...!pyramid!thirdi!peter

davidra@batcomputer.tn.cornell.edu (David A. Rabson) (09/02/88)

(Summary: people complain about lack of variable-length arg lists for
 macros.  Standards people try to cover themselves.  Other people point
 out that (( ... )) often works almost as well as variable-length arg list.)

There is another way to fake out at least some preprocessors.  I find the
following useful in header files.  The header file is included in several
code files only one of which defines GLOBALS.

#undef INIT
#undef COMMA
#define COMMA ,

#ifdef GLOBALS
#define INIT(x) =x
#else
#define INIT(x) /*nop*/
#endif

extern int x[2] INIT({1 COMMA 2});


Since the (( )) and COMMA kludges cover all the cases where I might have
wanted variable-length argument lists in cpp macros, I have to agree
that the extension would be unnecessary

				David Rabson
				Laboratory of Atomic and Solid State Physics

walker@island.uu.net (Richard Walker) (09/15/88)

In article <228@itivax.UUCP> scs@itivax.UUCP (Steve C. Simmons) writes:
>In article <1036@cbnews.ATT.COM> lvc@cbnews.ATT.COM (Lawrence V. Cipriani) writes:
>-In article <438@ucsvc.unimelb.edu.au>, u5565522@ucsvc.unimelb.edu.au (David Clunie) writes:
>-
>-	#ifdef TRACE
>-	#define trace(anything)	anything
>-	#else
>-	#define trace(anything)
>-	#endif
>-
>	if ( x == m )
>		trace( printf( "the consequences\n) ; )
>	froob( some function ) ;
>
>This produced rather different programs depending on the definition of
>'trace'!

The problem illustrated above is caused by the bad usage of the macro. The
macro call should be formed as a statement to avoid the flow control problem.
In other words, the errors above are caused by the missing ';' at the end
of the trace macro call.

Especially, macros which expand into code with flow control
should be formed such that the semicolon cleanly terminates
the flow, e.g.:

#define COMPLEX_MACRO(foo) (if(SOME_TEST(foo)) {statements;}else)

Then the correct usage of COMPLEX_MACRO would be:
COMPLEX_MACRO(foo);
which also handles the case where COMPLEX_MACRO expands into nothing.

cat <flames_about_crufty_usage_of_code_in_macros >/dev/null

chris@mimsy.UUCP (Chris Torek) (09/15/88)

In article <362@island.uu.net> walker@island.uu.net (Richard Walker)
suggests that
>... macros which expand into code with flow control
>should be formed such that the semicolon cleanly terminates
>the flow, e.g.:
>
>#define COMPLEX_MACRO(foo) (if(SOME_TEST(foo)) {statements;}else)

This works (after correcting the syntax), but I recommend instead

	#define MAC() if (!(normal_test)) /*void*/; else mac_statement

or, if you need {}s,

	#define	MAC() do { mac_statements } while (0)

These have the advantage over the one above that

	MAC()		/* missing semicolon */
	next_stmt;

produces a syntax error, rather than code that reads

	if (some_test) { statements; } else next_stmt;
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

scjones@sdrc.UUCP (Larry Jones) (09/16/88)

In article <362@island.uu.net>, walker@island.uu.net (Richard Walker) writes:
> The problem illustrated above is caused by the bad usage of the macro. The
> macro call should be formed as a statement to avoid the flow control problem.
> In other words, the errors above are caused by the missing ';' at the end
> of the trace macro call.
> 
> Especially, macros which expand into code with flow control
> should be formed such that the semicolon cleanly terminates
> the flow, e.g.:
> 
> #define COMPLEX_MACRO(foo) (if(SOME_TEST(foo)) {statements;}else)

Well, it's a good idea, but the example could be better - it could be valid
C if the replacement text didn't have the parentheses around a partial
statement!  In any case, the preferred definition of statement macros is:

#define COMPLEX_MACRO(foo) do { whatever } while (0)

That way the macro expands into a single statement, eats the following semi-
colon, and causes a syntax error if there isn't one instead of accidentally
swallowing the following statement.

----
Larry Jones                         UUCP: uunet!sdrc!scjones
SDRC                                      scjones@sdrc.uucp
2000 Eastman Dr.                    BIX:  ltl
Milford, OH  45150                  AT&T: (513) 576-2070
"Save the Quayles" - Mark Russell

david@sun.uucp (David DiGiacomo) (09/16/88)

In article <13573@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>or, if you need {}s,
>
>	#define	MAC() do { mac_statements } while (0)

That's nice, but lint churlishly complains about "constant in conditional
context".  To avoid this, use:

#ifdef lint
int _ZERO_;
#else
#define _ZERO_ 0
#endif

#define _STMT(op)	do { op } while (_ZERO_)

#define	MAC()		_STMT(mac_statements)

jones@ingr.UUCP (Mark Jones) (09/16/88)

In article <228@itivax.UUCP> scs@itivax.UUCP (Steve C. Simmons) writes:
>In article <1036@cbnews.ATT.COM> lvc@cbnews.ATT.COM (Lawrence V. Cipriani) writes:
>-In article <438@ucsvc.unimelb.edu.au>, u5565522@ucsvc.unimelb.edu.au (David Clunie) writes:
>-
>-	#ifdef TRACE
>-	#define trace(anything)	anything
>-	#else
>-	#define trace(anything)
>-	#endif
>-
>	if ( x == m )
>		trace( printf( "the consequences\n) ; )
>	froob( some function ) ;
>
This produced rather different programs depending on the definition of
'trace'!
 
Try rephrasing it as

	if(x == m)
		trace(printf("the consequences\n"));
						   ^
						   ^
						   ^
This will evaluate to:

	if(x == m)
		;
or
	if(x == m)
		printf("the cosequences\n");
				
						  
which will not affect the surrounding code.

karl@haddock.ima.isc.com (Karl Heuer) (09/17/88)

The original question in this thread, concerning an idea for compiler support
for variadic macros, was appropriate for comp.std.c.  The stream of replies
that look like `This is how I like to work around the problem' are not; these
should be confined to comp.lang.c.  Please stop the crossposting.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint