[comp.unix.wizards] #defines with variable # arguments

lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) (05/12/88)

    Are there any versions of cpp which allow one to define macros which
accept variable numbers of arguments?  I keep wishing for this every time
I try to move code developed using sysV sprintf to a BSD system, so that
I could do something like:

#ifdef	BSD_TO_5_SPRINTF
char	*(*Sprintf)() = sprintf ;
#define sprintf(...)		strlen (Sprintf (...))
#endif

#ifdef	5_TO_BSD_SPRINTF
int	(*Sprintf)() = sprintf ;
#define sprintf(str,...)	((void) Sprintf (str,...), str)
#endif

    I know that some systems have `vsprintf' or some such thing which
can be called from a sprintf replacement routine, but since the vprintf
routines are not universally available (sadly), using them seems only
to add to portability problems.

    Does anyone know why the folks at Berkeley chose to have their
sprintf return its first argument, instead of the number of characters
printed?  I can't think of any good reason for this choice, since it
throws away valuable information (the # of characters printed) which
is painful to reacquire.

    On a separate topic: How common is the ability to initalize unions?
(In C, of course.)  Did this make it into the ANSI specs?

------------------------------------------------------------------------
Laurence G. Yaffe			lgy@pupthy.princeton.edu
Department of Physics			lgy@pucc.bitnet
Princeton University			...!princeton!pupthy!lgy
PO Box 708, Princeton NJ 08544		609-452-4371 or -4400

guy@gorodish.Sun.COM (Guy Harris) (05/12/88)

>     Are there any versions of cpp which allow one to define macros which
> accept variable numbers of arguments?

None that I know of.  There are tricks that can be used for this:

#define	Sprintf(x)	sprintf x

main()
{
	char buf[40];

	Sprintf((buf, "%d", 33));
	Sprintf((buf, "%d %d", 33, 66));
}

but this requires the extra layer of parentheses; I presume you want a
"magic bullet" that requires few source changes to the program you're trying to
convert.

Neither K&R nor ANSI C have any syntax for macros of this sort.

> I keep wishing for this every time I try to move code developed using sysV
> sprintf to a BSD system,

Your best bet here is just to redo the code not to depend on the return value
of "sprintf".  Sad, but true.

>     Does anyone know why the folks at Berkeley chose to have their
> sprintf return its first argument, instead of the number of characters
> printed?

Nobody can possibly know that, because they weren't the ones who made that
decision.  "sprintf" worked that way in V7 (although I don't think it was so
documented) - in fact, the System III SCCS code was written assuming this
behavior, which is kind of amusing since the System III "sprintf" returned the
number of characters generated....  (Some BSD/S5 differences are really V7/S5
differences; questions about why *those* differences exist should be directed
to AT&T, since they were responsible for both systems.)

lvc@tut.cis.ohio-state.edu (Lawrence V. Cipriani) (05/12/88)

In article <2855@phoenix.Princeton.EDU>, lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) writes:
> 
>     Are there any versions of cpp which allow one to define macros which
> accept variable numbers of arguments?  I keep wishing for this every time
> I try to move code developed using sysV sprintf to a BSD system, so that
> I could do something like:
>	examples deleted

This question comes up now and then.  No version of cpp I know has a
variable count capability, but those are only AT&T cpp's.  You can
work around it though, so this is portable.  Define a macro like this:

#define SPRINTF(args)	sprintf args

and use it like this:

	SPRINTF((str, format, a1, a2, ..., an));

The double parens trick cpp into seeing only one argument.  After
cpp expansion you get:

	sprintf (str, format, a1, a2, ..., an);
-- 
Larry Cipriani, AT&T Network Systems and Ohio State University
Domain: lvc@tut.cis.ohio-state.edu
Path: ...!cbosgd!osu-cis!tut.cis.ohio-state.edu!lvc (weird but right)

terryl@tekcrl.TEK.COM (05/14/88)

In article <2855@phoenix.Princeton.EDU+ lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) writes:
+
+    Are there any versions of cpp which allow one to define macros which
+accept variable numbers of arguments?  I keep wishing for this every time
+I try to move code developed using sysV sprintf to a BSD system, so that
+I could do something like:
+
+#ifdef	BSD_TO_5_SPRINTF
+char	*(*Sprintf)() = sprintf ;
+#define sprintf(...)		strlen (Sprintf (...))
+#endif
+
+#ifdef	5_TO_BSD_SPRINTF
+int	(*Sprintf)() = sprintf ;
+#define sprintf(str,...)	((void) Sprintf (str,...), str)
+#endif

     I came across this kludge years ago, hope it helps:
#define	sprintf(prf)	{ Sprintf prf }

     The TRICK is to ADD an extra level of parentheses, using it like thus:

		sprintf( (buffer,"<what-ever-string-you-want>",<varargs>) );

This makes cpp THINK there is only one argument to the macro; I know you
don't want to muck with the sources and just want to put in the #define
and be done with it, but this is the only way I know of.

+    I know that some systems have `vsprintf' or some such thing which
+can be called from a sprintf replacement routine, but since the vprintf
+routines are not universally available (sadly), using them seems only
+to add to portability problems.
+
+    Does anyone know why the folks at Berkeley chose to have their
+sprintf return its first argument, instead of the number of characters
+printed?  I can't think of any good reason for this choice, since it
+throws away valuable information (the # of characters printed) which
+is painful to reacquire.

     How painful is a strlen???? (I.E. I really don't understand your
complaint). Since Berkeley code is almost always derived upon ATT (or
whatever they're calling themselves this year!!! (-:) code, it sounds
like an earlier version of sprintf returned its first argument. However,
I also can't really think of any good reason for one return value over
the other.......

jgy@hropus.UUCP (John Young) (05/14/88)

> 
> >     Are there any versions of cpp which allow one to define macros which
> > accept variable numbers of arguments?

I think that some of the AT&T CPLU compilers(cpp's) allow this,
you just overdeclare the arguments:

#define   Dprintf(a,b,c,d,e,f,g,h,i) {if (Debug) printf(a,b,c,d,e,f,g,h,i);}

and compile.  You will get a warning about each source line which uses
the wrong number of arguments.


 

lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) (05/14/88)

In article <2633@tekcrl.TEK.COM> terryl@tekcrl.tek.com writes:
 >In article <2855@phoenix.Princeton.EDU+ (lgy@pupthy2.PRINCETON.EDU) I wrote:
 >+
 >+    Are there any versions of cpp which allow one to define macros which
 >+accept variable numbers of arguments?  I keep wishing for this every time
 >+I try to move code developed using sysV sprintf to a BSD system, so that
 >+I could do something like:	[example deleted]
 >
 >     I came across this kludge years ago, hope it helps:
 >#define	sprintf(prf)	{ Sprintf prf }
 >
 >     The TRICK is to ADD an extra level of parentheses, using it like thus:
 >
 >		sprintf( (buffer,"<what-ever-string-you-want>",<varargs>) );
 >
 >This makes cpp THINK there is only one argument to the macro; I know you
 >don't want to muck with the sources and just want to put in the #define

	Right!!

 >and be done with it, but this is the only way I know of.

    A number of people suggested variants of the above.  Unfortunately,
all of these approaches require manual editing of (large amounts of) existing
source code.  The whole point of the exercise is to be able to automate the
conversion between sysV & BSD versions of sprintf.  Regrettably, such a
stupid little inconsistency (the difference in return values) cannot be
automatically fixed using any combination of cpp, sed, or awk.

    Several other people pointed out that one shouldn't ever use the
return value of sprintf if you want maximally portable code.
True, but rewritting existing code is SO much fun :-).

 >+    Does anyone know why the folks at Berkeley chose to have their
 >+sprintf return its first argument, instead of the number of characters
 >+printed?  I can't think of any good reason for this choice, since it
 >+throws away valuable information (the # of characters printed) which
 >+is painful to reacquire.
 >
 >     How painful is a strlen???? (I.E. I really don't understand your
 >complaint).

    Well, since every implementation of sprintf I've seen counts the
number of characters printed internally, its irritating if you have to
call strlen just to recompute information that was easily available but
thrown away.  And its quite possible for the extra call to strlen to
take nearly as long as the sprintf.  If you're calling sprintf a LOT of
times, adding a call to strlen after each sprintf can be very significant.

------------------------------------------------------------------------
Laurence G. Yaffe			Internet: lgy@pupthy.princeton.edu
Department of Physics			Bitnet:   lgy@pucc
Princeton University			UUCP:     ...!princeton!pupthy!lgy
PO Box 708, Princeton NJ 08544

ok@quintus.UUCP (Richard A. O'Keefe) (05/14/88)

In article <2855@phoenix.Princeton.EDU>, lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) writes:
>     Does anyone know why the folks at Berkeley chose to have their
> sprintf return its first argument, instead of the number of characters
> printed?  I can't think of any good reason for this choice, since it
> throws away valuable information (the # of characters printed) which
> is painful to reacquire.

Berkeley left sprintf() as they found it; that's what it did in V7.
There is an argument in favour of it: it makes it more like strcpy().
[Not that I think much of it, I've never found the return value of
 strcpy() useful.]  What's so painful about calling strlen()?

ok@quintus.UUCP (Richard A. O'Keefe) (05/15/88)

In article <2886@phoenix.Princeton.EDU>, lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) writes:
> In article <2633@tekcrl.TEK.COM> terryl@tekcrl.tek.com writes:
>  >In article <2855@phoenix.Princeton.EDU+ (lgy@pupthy2.PRINCETON.EDU) I wrote:
>  >+    Are there any versions of cpp which allow one to define macros which
>  >+accept variable numbers of arguments?>  >     I came across this kludge years ago, hope it helps:
>  >     The TRICK is to ADD an extra level of parentheses, using it like thus:
>  >		sprintf( (buffer,"<what-ever-string-you-want>",<varargs>) );
>  >This makes cpp THINK there is only one argument to the macro; I know you
>  >don't want to muck with the sources and just want to put in the #define
>     A number of people suggested variants of the above.

Just a wild heretical suggestion, but there is *another* macro processor
which comes standard with UNIX.  System V has it and BSD has it, and there
is a rather nice public domain version.  I refer, of course, to m4.

What I suggest is a one-time conversion:
	sprintf(S,F)		-> sprintf0(S,F)
	sprintf(S,F,A)		-> sprintf1(S,F,A)
	...
	sprintf(S,F,A,B,C,D,E,F)-> sprintf6(S,F,A,B,C,D,E,F)
however many you need.  This is easy enough to do with m4.  Then you write
your cpp macros for each sprintfN, which is a bit of a pain, but means you
don't have to keep using m4.

It is precisely because m4 is there to fall back on when you need REAL
macro-power that cpp can be so weak.

ok@quintus.UUCP (Richard A. O'Keefe) (05/15/88)

In article <979@cresswell.quintus.UUCP>, ok@quintus.UUCP (Richard A. O'Keefe) writes:
> What I suggest is a one-time conversion:
> 	sprintf(S,F)		-> sprintf0(S,F)
> 	sprintf(S,F,A)		-> sprintf1(S,F,A)
> 	...
> 	sprintf(S,F,A,B,C,D,E,F)-> sprintf6(S,F,A,B,C,D,E,F)
> however many you need.  This is easy enough to do with m4.

I should substantiate that.

cat >sprintf.m4 <<'EOF'
define(sprintf,``sprintf'eval($#-2)($*)')dnl
EOF

This uses $# and $* which are in the System V and PD versions, but not in
the BSD version, so you may have to do your conversion on System V.
All it takes is
	m4 sprintf.m4 original.c >converted.c

If you just want to make the BSD sprintf() look as though it returned an
integer, do

cat >sprintflen.m4 <<'EOF'
define(sprintf,`strlen(`sprintf'($*))')dnl
EOF

lgy@pupthy2.PRINCETON.EDU (Larry Yaffe) (05/16/88)

In article <980@cresswell.quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes:
    [Using m4 to process macros with variable # arguments]

-I should substantiate that.
-
-If you just want to make the BSD sprintf() look as though it returned an
-integer, do
-
-cat >sprintflen.m4 <<'EOF'
-define(sprintf,`strlen(`sprintf'($*))')dnl
-EOF

    This was definitely a helpful suggestion - not being overly familiar
with m4, I hadn't considered using it.  However, the suggested m4 scripts
suffer from two small problems:

    Whitespace between the "sprintf" and the following argument list must
first be removed in order for m4 to process the arguments correctly.
This is easily accomplished using 'sed'.

    Argument lists containing unmatched quoted parentheses, such as,
sprintf (str,"= ("),  produce horrible garbage.  This is a real occurence
in some of the source I'm dealing with.  (However, having to edit a
handful of special sprintf's by hand is a big improvement over having
to edit hundreds.)

------------------------------------------------------------------------
Laurence G. Yaffe			Internet: lgy@pupthy.princeton.edu
Department of Physics			Bitnet:   lgy@pucc
Princeton University			UUCP:     ...!princeton!pupthy!lgy
PO Box 708, Princeton NJ 08544

gandalf@csli.STANFORD.EDU (Juergen Wagner) (05/17/88)

To put my five cents into this discussion:

I prefer to use a simple macro like
	# ifdef DEBUG
	#   define DBG	do_debug
	# else !DEBUG
	#   define DBG	if (0) do_debug
	# endif DEBUG
This will expand the lines of the form
	DBG(flag, "sorry, the flag is %d", flag);
into a line which calls my function do_debug only if DEBUG is set. Of course,
do_debug is a function with a variable number of arguments.

I know, there are applications where this is not suffifient, and I know one
can use double parens, ... This posting is a comment and not a dogma!

-- 
Juergen "Gandalf" Wagner,		   gandalf@csli.stanford.edu
Center for the Study of Language and Information (CSLI), Stanford CA

daveb@llama.rtech.UUCP (It takes a clear mind to make it) (05/17/88)

In article <152@hropus.UUCP> jgy@hropus.UUCP (John Young) writes:
>> >     Are there any versions of cpp which allow one to define macros which
>> > accept variable numbers of arguments?
>I think that some of the AT&T CPLU compilers(cpp's) allow this,
>you just overdeclare the arguments:
>
>#define   Dprintf(a,b,c,d,e,f,g,h,i) {if (Debug) printf(a,b,c,d,e,f,g,h,i);}
>
>and compile.  You will get a warning about each source line which uses
>the wrong number of arguments.

If you do 

#define Dprintf	if(Debug) printf

you don't get any warnings; you do get possible dangling else problems. 
This has rarely been a problem on the occasions I've usedthis technique.

-dB

{amdahl, cpsc6a, mtxinu, sun, hoptoad}!rtech!daveb daveb@rtech.uucp

ed@mtxinu.UUCP (Ed Gould) (05/18/88)

>If you do 
>
>#define Dprintf	if(Debug) printf
>
>you don't get any warnings; you do get possible dangling else problems. 

To avoid the dangling-else problem, use

	#define	Dprintf	if(Debug == 0); else printf

for the same effect.

-- 
Ed Gould                    mt Xinu, 2560 Ninth St., Berkeley, CA  94710  USA
{ucbvax,uunet}!mtxinu!ed    +1 415 644 0146

"I'll fight them as a woman, not a lady.  I'll fight them as an engineer."

dgk@ulysses.homer.nj.att.com (David Korn[eww]) (05/21/88)

Glenn Fowler, author of nmake (4th generation make) has written an
ANSI cpp with several nice extensions.  One of them is
#define foo(a,...)

David Korn
ulysses!dgk