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

u5565522@ucsvc.unimelb.edu.au (David Clunie) (08/31/88)

How do people feel about the idea of preprocessor macros with variable
length argument lists ?

The concept has already been enshrined for true functions, allowing
protoypes for things like the printf() family of functions, and a set of
portable functions and macros to access the list of arguments.

However this can not be done with macro calls. Wouldn't it be nice to be
able to do something like ...


	#ifdef TRACE

	#define	tracef(s,...)	printf(s,...)

	#else

	#define	tracef(s,...)	/* nothing */

	#endif


At present, this CANNOT BE DONE, without nesting parentheses (clumsy) or
using a call to an empty function (inefficient, unless you have inline
integration in an optimizing compiler).

Just a thought. Probably a bit late for the ANSI standard. I am certainly
going to put it in my compiler though !!!

	Regards ... David Clunie

gwyn@smoke.ARPA (Doug Gwyn ) (09/01/88)

In article <438@ucsvc.unimelb.edu.au> u5565522@ucsvc.unimelb.edu.au (David Clunie) writes:
>Just a thought. Probably a bit late for the ANSI standard.

It was brought up, thoroughly discussed, and eventually rejected by X3J11.

rss@ece-csc.UUCP (ML) (09/01/88)

In a previous article u5565522@ucsvc.unimelb.edu.au (David Clunie) wrote:
>How do people feel about the idea of preprocessor macros with variable
>length argument lists ?


I for one would love it.  Plenty of times I've wanted macros like:

#define trace(...)  if (TraceFlag) fprintf(stderr, ...)
#define crash(rc,...)  { fprintf(stderr,...); return(rc); }

etc. etc.

This seems like it would be a simple and useful addition to the preprocessor,
and one which could be implemented in a fairly painless way.  Would it really
be that difficult to add to the standard?  (No flames please, that's a 
serious question based upon my own ignorance :-)

One yes vote here...

             Mark Lanzo
     borrowing      ...!mcnc!ece-csc!rss

lvc@cbnews.ATT.COM (Lawrence V. Cipriani) (09/01/88)

In article <438@ucsvc.unimelb.edu.au>, u5565522@ucsvc.unimelb.edu.au (David Clunie) writes:
> How do people feel about the idea of preprocessor macros with variable
> length argument lists ?
I'd like it, but its not essential.  I don't mind using double parenthesis
to fake out the preprocessor.

> However this can not be done with macro calls. Wouldn't it be nice to be
> able to do something like ...
> 
> 	#ifdef TRACE
> 	#define	tracef(s,...)	printf(s,...)
> 	#else
> 	#define	tracef(s,...)	/* nothing */
> 	#endif
> 
> At present, this CANNOT BE DONE, without nesting parentheses (clumsy) or
> using a call to an empty function (inefficient, unless you have inline
> integration in an optimizing compiler).

Only clumsy for the uncoordinated :-)  You missed one more way:

	#ifdef TRACE
	#define trace(anything)	anything
	#else
	#define trace(anything)
	#endif

and you use it like this:

	c(i)
	{
		trace( if (i == 0) printf("boom\n");)
	}

The only restriction I can think of now is that you'll have to be careful
how the comma operator is used.

-- 
Larry Cipriani, AT&T Network Systems, Columbus OH, cbnews!lvc lvc@cbnews.ATT.COM

gsf@ulysses.homer.nj.att.com (Glenn Fowler[eww]) (09/01/88)

In article <438@ucsvc.unimelb.edu.au>, u5565522@ucsvc.unimelb.edu.au (David Clunie) writes:
> ... Wouldn't it be nice to be able to do something like ...
> 	#ifdef TRACE
> 	#define	tracef(s,...)	printf(s,...)
> 	#else
> 	#define	tracef(s,...)	/* nothing */
> 	#endif
> ... Probably a bit late for the ANSI standard ...

too late, but here is the description of macro `...' args for
a local preprocessor extension:

	If the last formal argument is followed by the `...'
	token then it is replaced by the expanded value
	of all remaining actual arguments and intervening `,'
	tokens from the macro call.  If there is only one formal
	argument then the macro may be called with no actual
	arguments, otherwise there must be at least one actual
	argument for the last formal argument.

this would change the tracef() macro definition to

	#define tracef(s,...)	printf(s)

the `,' before the `...' is optional to satisfy all tastes
-- 
    Glenn Fowler                    AT&T Bell Laboratories, Murray Hill
    {ihnp4,allegra}!ulysses!gsf                          (201)-582-2195

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

d85_kitte@tekn01.chalmers.se (Kristian Wedberg) (09/02/88)

In article <438@ucsvc.unimelb.edu.au>, u5565522@ucsvc.unimelb.edu.au (David Clunie) writes:
> How do people feel about the idea of preprocessor macros with variable
> length argument lists ?
> 
> The concept has already been enshrined for true functions, allowing
> protoypes for things like the printf() family of functions, and a set of
> portable functions and macros to access the list of arguments.
> 
> However this can not be done with macro calls. Wouldn't it be nice to be
> able to do something like ...
> 
> 
> 	#ifdef TRACE
> 
> 	#define	tracef(s,...)	printf(s,...)
> 
> 	#else
> 
> 	#define	tracef(s,...)	/* nothing */
> 
> 	#endif
> 
	...
 
> 	Regards ... David Clunie


My solution for the debugging-part of this is to use several macros.
The following is a (very) small extract from my debug-includefile:


#ifndef DEBUG_H
#define DEBUG_H

/* name:  debug.h
 *
 * description:  Header-file with debug-macros & prototypes.
 *
	pr(text)		: print text (no " " or \n, & NO COMMAS!)
	pr0("..\n") - pr6(...)	: print string (with 0 - 6 arguments)
 *
 * problems:
 *	??? No commas in pr() ???
 */

/* pr(), pr1() -> pr6()
 * Produces no code when NPRINTING is defined! Just an empty statement
 * from the semicolon.
 */

/*	Print without arguments. "..." NOT needed */
#ifndef NPRINTING
#define pr(s)	printf("s\n")
#else
#define pr(x)
#endif

/*	Print with 0 to 6 arguments & "...\n" */
#ifndef NPRINTING
#define pr0(s)				printf(s)
#define pr1(s,a1)			printf(s,a1)
#define pr1(s,a1)			printf(s,a1)
#define pr2(s,a1,a2)			printf(s,a1,a2)
#define pr3(s,a1,a2,a3)			printf(s,a1,a2,a3)
#define pr4(s,a1,a2,a3,a4)		printf(s,a1,a2,a3,a4)
#define pr5(s,a1,a2,a3,a4,a5)		printf(s,a1,a2,a3,a4,a5)
#define pr6(s,a1,a2,a3,a4,a5,a6)	printf(s,a1,a2,a3,a4,a5,a6)
#else
#define pr0(x)
#define pr1(x,x1)
#define pr2(x,x1,x2)
#define pr3(x,x1,x2,x3)
#define pr4(x,x1,x2,x3,x4)
#define pr5(x,x1,x2,x3,x4,x5)
#define pr6(x,x1,x2,x3,x4,x5,x6)
#endif


Hope you'll find it useful...

					Kristian Wedberg

gregg@ihlpb.ATT.COM (Wonderly) (09/02/88)

From article <1036@cbnews.ATT.COM>, by lvc@cbnews.ATT.COM (Lawrence V. Cipriani):
] In article <438@ucsvc.unimelb.edu.au>, u5565522@ucsvc.unimelb.edu.au (David Clunie) writes:
]
] ....
] 
]> However this can not be done with macro calls. Wouldn't it be nice to be
]> able to do something like ...
]> 
]> 	#ifdef TRACE
]> 	#define	tracef(s,...)	printf(s,...)
]> 	#else
]> 	#define	tracef(s,...)	/* nothing */
]> 	#endif
]> 
]> At present, this CANNOT BE DONE, without nesting parentheses (clumsy) or
]> using a call to an empty function (inefficient, unless you have inline
]> integration in an optimizing compiler).
] 
] Only clumsy for the uncoordinated :-)  You missed one more way:
] 
] 	#ifdef TRACE
] 	#define trace(anything)	anything
] 	#else
] 	#define trace(anything)
] 	#endif
] 
] and you use it like this:
] 
] 	c(i)
] 	{
] 		trace( if (i == 0) printf("boom\n");)
] 	}

I always use the following

#ifdef	DEBUG
#define	debug(x,y)	{if (x > debuglvl) printf y};
#else	DEBUG
#define	debug(x,y)
#endif	DEBUG

This gets you something like the above, although it does not allow
arbitrary values or conditions to govern the printing or execution
of debug information/code.  In general though I seldom need other
conditions to govern the debug output because I try to check all of
the conditions where things might go wrong anyway so I would write
the above as...

c(i)
{
	if (i == 0) {
		debug (1, ("OOOPS i == %d\n", i));
		abort();
	}
}

Gregg Wonderly
AT&T Bell Laboratories                   DOMAIN: gregg@ihlpb.att.com
IH2D217 - (312) 979-2794                 UUCP:   ihnp4!ihlpb!gregg

-- 
Gregg Wonderly
AT&T Bell Laboratories                   DOMAIN: gregg@ihlpb.att.com
IH2D217 - (312) 979-2794                 UUCP:   ihnp4!ihlpb!gregg

meissner@xyzzy.UUCP (Michael Meissner) (09/03/88)

In article <438@ucsvc.unimelb.edu.au> u5565522@ucsvc.unimelb.edu.au (David Clunie) writes:
| How do people feel about the idea of preprocessor macros with variable
| length argument lists ?
| 
| The concept has already been enshrined for true functions, allowing
| protoypes for things like the printf() family of functions, and a set of
| portable functions and macros to access the list of arguments.

As Doug mentioned this has come before the ANSI committee and did not get
the necessary 2/3 vote to get it in.  Let me mention something that came
up in our discussion:  Namely that it is desirable that you have two
different forms of this, one for the times that you want the comma supplied
if there are extra arguments, and the other for times when you don't want
the comma.  Otherwise you could get compilation errors if no extra arguments
were passed.
-- 
Michael Meissner, Data General.

Uucp:	...!mcnc!rti!xyzzy!meissner
Arpa:	meissner@dg-rtp.DG.COM   (or) meissner%dg-rtp.DG.COM@relay.cs.net

scs@itivax.UUCP (Steve C. Simmons) (09/06/88)

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:
-> How do people feel about the idea of preprocessor macros with variable
-> length argument lists ?
-> 
-> At present, this CANNOT BE DONE, without nesting parentheses (clumsy) or
-> using a call to an empty function (inefficient, unless you have inline
-> integration in an optimizing compiler).
-
-Only clumsy for the uncoordinated :-)  You missed one more way:
-
-	#ifdef TRACE
-	#define trace(anything)	anything
-	#else
-	#define trace(anything)
-	#endif
-
-and you use it like this:
-
-	c(i)
-	{
-		trace( if (i == 0) printf("boom\n");)
-	}
-
-The only restriction I can think of now is that you'll have to be careful
-how the comma operator is used.
-
-Larry Cipriani, cbnews!lvc lvc@cbnews.ATT.COM

A nice hack, but one additional word of warning -- the disappearing line
of code can cause some interesting and obscure bugs.  I had one program
that worked with the trace code but failed oddly without it.  The exact
form escapes me, but it was something like

	. . .
	if ( x == m )
		trace( printf( "the consequences\n) ; )
	froob( some function ) ;

This produced rather different programs depending on the definition of
'trace'!

-- 
Steve Simmons		...!umix!itivax!vax3!scs
Industrial Technology Institute, Ann Arbor, MI.
"You can't get here from here."

merlyn@rose3.rosemount.com (Brian Westley) (09/06/88)

There is a way to do this, but it's ugly, dangerous,
noisy, and not portable...

short	_NARGS_;
#define	max(a,b,c,d,e) \
    (_NARGS_=5,-a-_NARGS_,-b-_NARGS_,-c-_NARGS_,-d-_NARGS_,-e-_NARGS_,\
    _max(_NARGS_,a-0,b-0,c-0,d-0,e-0))

main()
{
	int x,y;

	printf("%d ",max(3,5,4));
	x = max((-9),(-11),2*6-55);
	y = max(4,2-x,(-6),999,5-9);
	printf("%d %d %d\n",x,y,max(x,y-3));
}

_max(n,a,b,c,d,e)
int n;
int a,b,c,d,e;
{
	int result;

	result = a;
	if (--n>0 && b>result) result=b;
	if (--n>0 && c>result) result=c;
	if (--n>0 && d>result) result=d;
	if (--n>0 && e>result) result=e;
	return result;
}

----
Merlyn LeRoy

haahr@phoenix.Princeton.EDU (Paul Gluckauf Haahr) (09/08/88)

> How do people feel about the idea of preprocessor macros with variable
> length argument lists ?
> 
> However this can not be done with macro calls. Wouldn't it be nice to be
> able to do something like ...
> 
> 
> 	#ifdef TRACE
> 	#define	tracef(s,...)	printf(s,...)
> 	#else
> 	#define	tracef(s,...)	/* nothing */
> 	#endif
> 
> At present, this CANNOT BE DONE, without nesting parentheses (clumsy) or
> using a call to an empty function (inefficient, unless you have inline
> integration in an optimizing compiler).

well, if your trace function has no more arguments than printf,
you can always do

	#ifdef	TRACE
	#define	tracef	printf
	#else
	#define	tracef	1 ? 0 :
	#endif

note the complete absence of parenthese.  if you want arguments,
the syntax gets ugly, but can work.  for example,

	#ifdef	TRACE
	#define	trace(n)	(debug > n) ? 0 : printf
	#else
	#define	trace(n)	1 ? 0 :
	#endif

usage is then

	trace(3)("n = %d\n", n)

making that print to stderr probably requires an additional function
call, but only when tracing is enabled.

[ this trick is not my own.  peter honeyman (now umix!honey) showed me
a variant in 1984 that is in the hdb uucp sources, used to implement
logging for uucico -x<number> ]

paul haahr
princeton!haahr		haahr@princeton.edu

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

In article <3604@phoenix.Princeton.EDU>, haahr@phoenix.Princeton.EDU (Paul Gluckauf Haahr) writes:
> > How do people feel about the idea of preprocessor macros with variable
> > length argument lists ?
> > 
> > However this can not be done with macro calls. Wouldn't it be nice to be
> > able to do something like ...
> > 
> > 
> > 	#ifdef TRACE
> > 	#define	tracef(s,...)	printf(s,...)
> > 	#else
> > 	#define	tracef(s,...)	/* nothing */
> > 	#endif
> > 
> > At present, this CANNOT BE DONE, without nesting parentheses (clumsy) or
> > using a call to an empty function (inefficient, unless you have inline
> > integration in an optimizing compiler).


If you want a trace function, try this one

#ifdef TRACE
#define TR(x) x
#else
#define TR(x)
#endif

	Now, you can just say
	

	.
	.
	.
	TR(printf("entering buggy function"));
	.
	.
	.

and all is well!


Bought from Dr Dobbs Journal, Only good thing to come out of that
magazine in months.(Did BYTE buy them out?)  <-- Rhetorical question

Mark Jones

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