[comp.lang.c] Passing Variable Numbers of Arguments

mackenzi@agnes.uucp (David MacKenzie) (01/07/89)

The following program produces unexpected results:

#include <varargs.h>

main ()
{
  printf ("foo:\n");
  foo (1, 2, 3, 0);
  printf ("bar:\n");
  bar (4, 5, 6, 0);
}

foo (va_alist)
  va_dcl
{
  bar (va_alist);
}

bar (va_alist)
  va_dcl
{
  va_list list;
  int i;

  va_start (list);
  while (i = va_arg (list, int))
    printf ("%d\n", i);
  va_end (list);
}

On a 68000 machine, it produces:

foo:
1
14679744
160
1
2
3
bar:
4
5
6

and on a VAX it produces:
foo:
1
bar:
4
5
6

whereas the output I wanted is:

foo:
1
2
3
bar:
4
5
6

In other words, if I call bar () directly, it works, but if I call it via
foo (), it breaks, in an implementation-dependant way, no less.  Why?

Is there anyway to get this to work?  The problem has arisen as I
am trying to port Allan Holub's (DDJ, April 1988) integer-only
printf from <stdarg.h> to <varargs.h>.  printf, sprintf, and fprintf
are small little functions that call a function called idoprnt and both the
small front-end functions and idoprnt seem to need to accept a variable
number of arguments.

David MacKenzie
edf@rocky2.rockefeller.edu
---
p.s. I post from this account because inews doesn't seem to be able to post
new articles on rocky2 -- very strange, since it can post followups just
fine.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/07/89)

In article <899@thor.stolaf.edu> edf@rocky2.rockefeller.edu writes:
>The following program produces unexpected results:
>foo (va_alist)
>  va_dcl
>{
>  bar (va_alist);
>}

No wonder!  The only proper way to pass a variable argument list
down another level is to pass an initialized va_list object.
The bar() function cannot be called both with variable arguments
AND with a single va_list argument.

Check out the specs for vfprintf() and friends to see how this
approach works.

ark@alice.UUCP (Andrew Koenig) (01/08/89)

In article <899@thor.stolaf.edu>, mackenzi@agnes.uucp (David MacKenzie) writes:

> The following program produces unexpected results:

> #include <varargs.h>

> main ()
> {
>   printf ("foo:\n");
>   foo (1, 2, 3, 0);
>   printf ("bar:\n");
>   bar (4, 5, 6, 0);
> }

> foo (va_alist)
>   va_dcl
> {
>   bar (va_alist);
> }

> bar (va_alist)
>   va_dcl
> {
>   va_list list;
>   int i;

>   va_start (list);
>   while (i = va_arg (list, int))
>     printf ("%d\n", i);
>   va_end (list);
> }

There is no way to pass your entire argument list to another function.
The closest you can come is to pass a va_list.  For example:

main ()
{
  printf ("foo:\n");
  foo (1, 2, 3, 0);
}

foo (va_alist)
  va_dcl
{
  va_list list;
  va_start (list);
  bar (list);
  va_end (list);
}

bar (x)
  va_list x;
{
  int i;

  while (i = va_arg (x, int))
    printf ("%d\n", i);
}
-- 
				--Andrew Koenig
				  ark@europa.att.com

chris@mimsy.UUCP (Chris Torek) (01/08/89)

In article <899@thor.stolaf.edu> mackenzi@agnes.uucp (David MacKenzie) writes:
>The following program produces unexpected results:
>foo (va_alist)
>  va_dcl
>{
>  bar (va_alist);
>}

This program is quite thoroughly illegal.  `va_alist' is allowed to be
defined as a `magic token' that tells the compiler `I accept variable
argument lists'; this magic token may well not be a valid argument.  If
you get lucky, the compiler will object to the call to bar().

>In other words, if I call bar () directly, it works, but if I call it via
>foo (), it breaks, in an implementation-dependant way, no less.  Why?

Because indirect calls to bar() can never pass a variable argument list.

>Is there anyway to get this to work?

You cannot call bar() from foo() (except with a fixed argument list).
What you CAN do is call a common routine from both foo() and bar():

foo(va_alist)
	va_dcl
{
	va_list ap;

	va_start(ap);
	vbar(ap);
	va_end(ap);
}

bar(va_alist)
	va_dcl
{
	va_list ap;

	va_start(ap);
	vbar(ap);
	va_end(ap);
}

vbar(ap)
	va_list ap;
{
	... code to deal with variable arguments ...
}

This is why vprintf, vfprintf, and vsprintf exist at all.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

chris@mimsy.UUCP (Chris Torek) (01/08/89)

In article <15341@mimsy.UUCP> I wrote:
>... indirect calls to bar() can never pass a variable argument list.

Oops, bad wording: *each* indirect call can pass a *different* fixed
argument list, but any one given indirect call can only pass *one* fixed
argument list.  So indirect calls (plural) can, but an indirect call
(singular) cannot.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

samperi@marob.MASA.COM (Dominick Samperi) (01/09/89)

In article <15341@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>[comments about System V style varargs usage...]

It seems that for the ANSI style varargs (that is, the one requiring
stdarg.h), at least one explicitly named arg must be included in the
function definition, so that there is a FIRST that can be supplied to
va_start, as in:

	va_list ap ;
	va_start(sp, FIRST) ;
	...
	x = va_arg(ap, TYPE) ;
	...
	va_end(ap) ;

Is this correct? If not, how does one define a function with a variable
number of args, for which there are no explicitly named args? The standard
documentation on this (K&R Second Edition, for example) does not make
this clear.

It also seems to be the case that it is not possible for a called function
(with a variable number of args) to determine how many args were actually
passed, or when the last arg has been fetched, unless this information is
supplied in the first parameter, say. Wouldn't it have been reasonable for
the standard to specify that the compiler should enable the called function
to determine the number of parameters that were passed, by automatically
passing this information as a first implicit parameter, for example?

Dominick Samperi
samperi@acf8.nyu.edu
uunet!hombre!samperi

chris@mimsy.UUCP (Chris Torek) (01/09/89)

In article <449@marob.MASA.COM> samperi@marob.MASA.COM (Dominick Samperi)
writes:
>It seems that for the ANSI style varargs (that is, the one requiring
>stdarg.h), at least one explicitly named arg must be included in the
>function definition [to provide an `anchor' for va_start] ....

Correct.

>... how does one define a function with a variable number of args,
>for which there are no explicitly named args?

I find it rather annoying that this is not possible.  A routine like
`make-vector', which takes a variable number of pointers and makes a
vector of pointers (with the last signified by a NULL pointer of the
appropriate type) must be written awkwardly instead of in the simple
fashion possible with <varargs.h>.

>Wouldn't it have been reasonable for the standard to specify that the
>compiler should enable the called function to determine the number of
>parameters that were passed ... ?

Possibly.  But that is not enough information: one needs not only the
number of parameters, but also their types.  It seems to me that doing
half the job (providing the count without the types) would be worse than
doing nothing at all.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

ark@alice.UUCP (Andrew Koenig) (01/09/89)

In article <449@marob.MASA.COM>, samperi@marob.MASA.COM (Dominick Samperi) writes:
> In article <15341@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:

> It seems that for the ANSI style varargs (that is, the one requiring
> stdarg.h), at least one explicitly named arg must be included in the
> function definition, so that there is a FIRST that can be supplied to
> va_start, as in:

> 	va_list ap ;
> 	va_start(sp, FIRST) ;

> 	x = va_arg(ap, TYPE) ;

> 	va_end(ap) ;

> Is this correct?

Yes.

>                  If not, how does one define a function with a variable
> number of args, for which there are no explicitly named args?

You can't.  The rationale is that there is no reason ever to do so.
After all, you have to know the type of the first argument somehow,
and without looking at the argument list.

> It also seems to be the case that it is not possible for a called function
> (with a variable number of args) to determine how many args were actually
> passed, or when the last arg has been fetched, unless this information is
> supplied in the first parameter, say.

Correct.

>                                        Wouldn't it have been reasonable for
> the standard to specify that the compiler should enable the called function
> to determine the number of parameters that were passed, by automatically
> passing this information as a first implicit parameter, for example?

Sure, but they didn't do it that way.

For one thing, that would slow down every call a little.
-- 
				--Andrew Koenig
				  ark@europa.att.com

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/09/89)

In article <449@marob.MASA.COM> samperi@marob.masa.com (Dominick Samperi) writes:
>It seems that for the ANSI style varargs (that is, the one requiring
>stdarg.h), at least one explicitly named arg must be included in the
>function definition, so that there is a FIRST that can be supplied to
>va_start, ... Is this correct?

Yes.  Some implementors claimed that this "hook" was necessary in order
for their <stdarg.h> implementation to have a reference point for va_arg
to use.  I think actually with certain compiler trickery this could have
been made invisible to the programmer, but the Committee was willing to
require the "anchor" argument(s) to be present.  I've been programming
variable-argument functions with code for both __STDC__ and old UNIX C
for quite some time now, and this constraint hasn't been much of a
problem.

>It also seems to be the case that it is not possible for a called function
>(with a variable number of args) to determine how many args were actually
>passed, or when the last arg has been fetched, unless this information is
>supplied in the first parameter, say.

Yup.  This reflects existing C practice.  PDP-11 UNIX had an nargs()
function that purported to return the argument count, but it didn't
work right (it returned the number of words of arguments, not the count).
Making this work right would have slowed down every function call.
nargs() was practically unused...

>Wouldn't it have been reasonable for the standard to specify that the
>compiler should enable the called function to determine the number of
>parameters that were passed, by automatically passing this information
>as a first implicit parameter, for example?

Why burden all implementations all the time when you can provide this
yourself as a patent argument in those cases where you need it?

You could also use a "sentinel" argument, e.g.
	concat(dest,"str_a","str_b","str_c",(char*)0);

ka@june.cs.washington.edu (Kenneth Almquist) (01/09/89)

Chris Torek gives some code for the situation in which one routine with
a variable number of arguments wants to have these arguments interpreted
by a different routine.  (See the end of this article.)  I've used the
same approach for lack of anything better, but is it portable?  The manual
page for varargs doesn't say anything about the effects of passing the
va_list variable to another routine.  The vprintf family of routines are
defined to accept va_list arguments, which should encourage implementors
to make this work, but doesn't force them to.  (They could have vprintf
perform some special operations to simulate the environment of its caller
before using va_arg.)  Perhaps the definition of the stdarg mechanism is
more specific?

Also, I'm curious about why Ansi C uses the stdarg mechanism instead of
varargs.  I would think that varargs would be easier to implement.  On
machines with simple calling conventions varargs requires no compiler
support; on other machines varargs is likely to require fewer special
cases than stdarg since either all or none of the arguments are handled
by the varargs mechanism.
					Kenneth Almquist




bar(va_alist)
	va_dcl
{
	va_list ap;

	va_start(ap);
	vbar(ap);
	va_end(ap);
}

vbar(ap)
	va_list ap;
{
	... code to deal with variable arguments ...
	... [uses va_arg(ap, type) to access arguments to "bar"] ...
}

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/09/89)

In article <6898@june.cs.washington.edu> ka@june.cs.washington.edu (Kenneth Almquist) writes:
>The manual page for varargs doesn't say anything about the effects of
>passing the va_list variable to another routine.

The varargs documentation left a lot unspecified, and consequently some
implementations indeed will not do the expected thing when you try to
pass a va_list to a subfunction.  However, such implementations need to
fix this anyway for their stdarg (ANSI C) version, and probably at the
same time their varargs macros can be fixed.  Many vendors already have
done so, motivated perhaps more by SVID compliance than anything else.

>Perhaps the definition of the stdarg mechanism is more specific?

Definitely.

>Also, I'm curious about why Ansi C uses the stdarg mechanism instead of
>varargs.  I would think that varargs would be easier to implement.

To the contrary, on some architectures <varargs.h> was much harder to
implement than <stdarg.h>.  I collected input from many C implementors
on behalf of X3J11, and had extended discussions about variable
arguments with several.  I believe <stdarg.h> is reasonable.

karl@haddock.ima.isc.com (Karl Heuer) (01/10/89)

I actually (slightly reluctantly) agree that it's not worthwhile to provide
nargs() or equivalent, but allow me to play Devil's Advocate for a bit...

In article <9317@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>In article <449@marob.MASA.COM> samperi@marob.masa.com (Dominick Samperi) writes:
>>It also seems to be the case that it is not possible for a called function
>>(with a variable number of args) to determine how many args were actually
>>passed, or when the last arg has been fetched, unless this information is
>>supplied in the first parameter, say.
>
>Yup.  This reflects existing C practice.  PDP-11 UNIX had an nargs()
>function that purported to return the argument count, but it didn't
>work right (it returned the number of words of arguments, not the count).
>Making this work right would have slowed down every function call.

Only calls to variadic functions; fixed-argument functions don't need the
feature, and it needn't be supplied for them.  (An ANSI compiler can tell
whether a function is variadic.)  Moreover, since it removes the need for a
sentinel on such functions as execl(), the cost of adding it as a pseudo-
argument should be offset by the benefit of removing the sentinel.  (And on
some implementations, the cost is already zero, so the benefit would be picked
up for free.)

(Lest someone feel the need to point this out: the major problem is that
execl() is already frozen; also, the printf() family doesn't pick up the
benefit, since there's no sentinel.)

>>Wouldn't it have been reasonable for the standard to specify [nargs]?
>
>Why burden all implementations all the time when you can provide this
>yourself as a patent argument in those cases where you need it?

Because computers are much better at counting things than humans are.  If some
of the arguments are ifdef'd out, getting the count right can be hairy.

>You could also use a "sentinel" argument, e.g.
>	concat(dest,"str_a","str_b","str_c",(char*)0);

This works as long as the arg type is something with an out-of-band value, but
it doesn't help if you want to write a function min(int, ...).

Chris Torek wrote:
>Possibly.  But that is not enough information: one needs not only the
>number of parameters, but also their types.  It seems to me that doing
>half the job (providing the count without the types) would be worse than
>doing nothing at all.

Yes, it does seem as though nargs() is not very useful for polymorphic
functions.  (And, as Doug pointed out, the number-of-words implementation is
bad--though this could be fixed by having nargs() return the number of bytes
so that the application could say nb -= sizeof(TYPE), assuming it knows what
type to expect next.)

Perhaps what we need is a separate type of declaration syntax for variadic,
automatically-argcounted, monomorphic functions; then only those functions
that use this new declaration would pay the price, if any, associated with
making the information available.

Fix it in `D'...

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

guy@auspex.UUCP (Guy Harris) (01/10/89)

 >>                                        Wouldn't it have been reasonable for
 >> the standard to specify that the compiler should enable the called function
 >> to determine the number of parameters that were passed, by automatically
 >> passing this information as a first implicit parameter, for example?
 >
...
 >For one thing, that would slow down every call a little.

To be fair, it wouldn't have to slow *every* call down, just calls to
functions for which a prototype is in scope that specifies that it is a
varargs function.... 

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/10/89)

In article <11378@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
>Yes, it does seem as though nargs() is not very useful for polymorphic
>functions.  (And, as Doug pointed out, the number-of-words implementation is
>bad--though this could be fixed by having nargs() return the number of bytes
>so that the application could say nb -= sizeof(TYPE), assuming it knows what
>type to expect next.)

No, if nargs() has any use at all it cannot be this.  The reason that
the va_ stuff must be used for portable access to arguments is that
many architectures do not have a simple, clean model for argument
alignment (which might be different from that indicated by sizeof),
and some pass some of the arguments different from others.  nbytes()
would be of no value in portable programming.

scs@adam.pika.mit.edu (Steve Summit) (01/10/89)

In article <8699@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes:
>There is no way to pass your entire argument list to another function.

Well, no socially acceptable way, maybe, but in the words of
Romeo Void, "Never say 'Never'."  I've got a routine called
"callg," named after the VAX instruction of the same name, which
lets you call an arbitrary function with an arbitrary number of
arguments.  It's sort of an inverse varargs.  Among other things,
I have used it to solve exactly the problem being discussed here,
namely to call a varargs function from a varargs function,
explicitly passing all of the first varargs function's arguments,
rather than indirectly through a va_list.

callg is actually implemented such that its invocation is (well,
could be, I haven't tried it everywhere) portable, although the
underlying implementation is obviously highly machine dependent.
(It's one of a handful of functions I know of that can't possibly
be written in C.)

                                            Steve Summit
                                            scs@adam.pika.mit.edu

friedl@vsi.COM (Stephen J. Friedl) (01/11/89)

In article <9321@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
> 
> The reason that
> the va_ stuff must be used for portable access to arguments is that
> many architectures do not have a simple, clean model for argument
> alignment (which might be different from that indicated by sizeof),
> and some pass some of the arguments different from others.

Doug is being polite here -- there are some *very* crazy
arg-passing schemes out there.

The Zilog Z8000 machine I used in school had an "efficient"
arg passing mechanism that passed the first few args in
registers.  There were six or so 16-bit registers allocated for
this, and 32-bit quantities passed in register pairs R0+R1 = RR0,
R2+R3=RR2, etc.  There were all kinds of rules for alignment
restrictions on this, *PLUS* the machine had a segmented and a
non-segmented mode which meant that pointers could be two
different lengths.  If they had varargs (it was Sys III), it must
have been hell to put together.  I do know that their _doprnt()
had *five* args to accomodate this.

I remember a time several years ago in school that I naively
claimed that taking the address of an arg and (*argp++) up the
stack was portable because "everybody passes args this way".  The
amazing follies of youth :-).

     Steve

-- 
Stephen J. Friedl        3B2-kind-of-guy            friedl@vsi.com
V-Systems, Inc.        I speak for me only      attmail!vsi!friedl
Santa Ana, CA  USA       +1 714 545 6442    {backbones}!vsi!friedl
-------Nancy Reagan on Usenix in San Diego: "Just say *go*"-------

seanf@sco.COM (Sean Fagan) (01/11/89)

>>                                        Wouldn't it have been reasonable for
>> the standard to specify that the compiler should enable the called function
>> to determine the number of parameters that were passed, by automatically
>> passing this information as a first implicit parameter, for example?
>For one thing, that would slow down every call a little.

Some machines (VAXen and WE32100's) already do this.

It doesn't slow the call down, since it's always there.  However, it could
be argued that the call could have been made faster by not wasting microcode
space...

-- 
Sean Eric Fagan | "Joy is in the ears that hear, not in the mouth that speaks."
seanf@sco.UUCP  |      -- Saltheart Foamfollower (Stephen R. Donaldson)
(408) 458-1422  | Any opinions expressed are my own, not my employers'.

guy@auspex.UUCP (Guy Harris) (01/12/89)

 >It doesn't slow the call down, since it's always there.  However, it could
 >be argued that the call could have been made faster by not wasting microcode
 >space...

Or by not wasting memory and CPU cycles fetching the count from the
instruction stream and stuffing it onto the stack.

ftw@masscomp.UUCP (Farrell Woods) (01/13/89)

In article <1002@vsi.COM> friedl@vsi.COM (Stephen J. Friedl) writes:
>
>The Zilog Z8000 machine I used in school had an "efficient"
>arg passing mechanism that passed the first few args in
>registers.

Some C compilers for more "conventional" machines also pass arguments
this way.  For instance, the Microware C compiler for their OS-9/68K
operating system will usually pass the first couple of args in d0 and d1,
and the rest on the stack.  Made for an interesting varargs...


-- 
Farrell T. Woods				Voice: (508) 692-6200 x2471
MASSCOMP Operating Systems Group		Internet: ftw@masscomp.com
1 Technology Way				uucp: {backbones}!masscomp!ftw
Westford, MA 01886				OS/2: Half an operating system

karl@haddock.ima.isc.com (Karl Heuer) (01/13/89)

In article <9321@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>In article <11378@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
>>[returning the total number of varargs bytes might be useful]
>
>No, if nargs() has any use at all it cannot be this.  The reason that
>the va_ stuff must be used for portable access to arguments is that
>many architectures do not have a simple, clean model for argument
>alignment (which might be different from that indicated by sizeof),
>and some pass some of the arguments different from others.  nbytes()
>would be of no value in portable programming.

I don't see any problem.  The proposal is that the variadic function knows the
sum of the sizes of the (widened) arguments in the nonfixed portion of the
arglist; given this information, the code
	nb = va_nbytes();
	while (format_indicates_another_arg) {
	    if (char_expected) {
		nb -= sizeof(int);
		ch = (char)va_arg(ap, int);
		do_char(ch);
	    } else if (long_expected) {
		nb -= sizeof(long);
		ii = va_arg(ap, int);
		do_long(ii);
	    }
	}
is portable.  (To be useful, add appropriate checks on the value of nb.)  If
the calling convention is such that the callee can compute the byte count,
then everything is okay.  If not, then the caller (which can easily compute
it) can supply it via a hidden argument.  It doesn't matter how kludgy the
argument passing mechanism is.

I'm not trying to claim that this would solve all of the problems with
variadic functions; this is just to demonstrate that it's not necessarily
fatal that the pdp11 nargs() can't count actual arguments.  Either a true arg
count or a byte count could plausibly be standardized (the caller knows both),
but on some implementations the byte count is already available without an
extra hook.

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

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/13/89)

In article <11410@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
>... given this information, the code ... is portable.

No it isn't!!  Apart from the obvious slip of picking up an int where
a long was required (which can be fixed), your use of sizeof to adjust
the byte count is flawed, because sizeof doesn't take into account
alignment requirements of the arguments, which can (and sometimes do)
differ from those of array members.  To make this work portably, you
would have to require implementations to provide at least one more
va_ function, but what's the point?  By that time, the postulated
utility of an argument byte count has been lost under a barrage of
details.

By the way, the alternate idea of casting argument addresses to
(char *) then subtracting them to count bytes fails due to not
knowing in advance what the type of the next argument is.  There may
be room for another argument of one type, but not of another type,
and unless you know more than byte counts you cannot determine
whether or not to pick up another argument.

dff@Morgan.COM (Daniel F. Fisher) (01/14/89)

In article <11410@haddock.ima.isc.com> karl@haddock.ima.isc.com
(Karl Heuer) proposes a va_nbytes() to be used in implementing
portable argument counting.

In article <9358@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn)
indicates that such a construct is not portable because use of
sizeof to adjust the argument byte count is flawed, due to the
alignment differences for array members and normal parameters.

Since arrays cannot be passed by value in C, I fail to see how this
is relevant.  However, I do agree with the conclusion, since one
can postulate calling sequences that are not strictly stack based.
Particularly in an interpreted environment or a massively parallel
architecture.

In evaluating proposals for a portable nargs() like mechanism, it is
necessary to keep in mind the purpose this mechanism.

If, on the one hand, the nargs() mechanism is to be used by functions
so that they do not have to rely on sentinel arguments or information
implicit in a required argument to determine how many arguments they
have, then a portable nargs() like mechanism is clearly required.
_IF_ this is the purpose, I would propose a macro, va_nargs(parg, type),
that evaluates to the number of arguments of the given type remaining.
By this, I do NOT mean to suggest that the calling sequence has to
identify the type of every argument.  All that is required, is that
the va_nargs() macro indicate how many successive va_arg(parg, type)
invocations would succeed given the current state of argument
processing.  But, if this _IS_ the purpose, I believe that these
varargs functions should require the caller to provide the argument
count explicitly as one of the required arguments, instead of
burdening the calling sequence with this overhead on EVERY function
call to ANY function.

If, on the other hand, the nargs() mechanism is to be used as a
run-time sanity check for adherence to a varargs calling convention
making use of a sentinel argument or relying on information present
in some required argument, then what is required is an assert() like
mechanism.  I would propose that the va_arg() macro be modified to
allow it to abort the program if it cannot return an argument of the
indicated type.  If the normal calling sequence does not provide
argument count information, then it would need to be augmented as
appropriate.  Since the new calling sequence and run time checking
in va_arg() might be too time consuming, their use would need to be
controlled by a compile time switch of some type.  Perhaps "#ifndef
NDEBUG" as is used by assert().  While this is easy to do when
selecting which form of the va_arg() macro to use, it presents an
interesting challenge in designing calling sequences.  The basic
problem is how to make the normal and augmented calling sequences
compatible.  (Incompatible calling sequences, would be a pain in the
head.)  I am not sure how to achieve compatibility without making the
normal calling sequence as inefficient as the augmented calling
sequence.

Calling Sequence Compatibility

If a function using the augmented calling sequence calls one expecting
the normal calling sequence, how does the called function ignore
argument count information?  If a function using the normal calling
sequence calls on using the augmented supporting calling sequence,
how does the called function process the arguments correctly and
avoid any argument count checking in va_arg()?  The answers may be
non-trivial.  If the argument count information is at the end of
the argument list, it cannot be located when it is needed.  If the
argument count information is at the beginning of the argument list,
then it cannot be distinguished from the first argument.  If the
argument count information is elsewhere in the argument list, then
there is a combination of these difficulties.  So the argument count
information would need to be kept somewhere else.  If it is a register,
then both calling sequences would need to set it to some meaningful
value.  If it is in a reserved place on the stack, the same is true.
What to do? What to do?

N.B.: By argument count information in the above, I refer to that
information required by va_arg() to determine if it is valid to return
an argument of the requested type.  The precise form this takes would
be an implementation issue.  Implementation in a 3B2 like environment
is straight forward, because the %AP (argument pointer) and %FP
(frame pointer) registers will bound the argument list in known way.
Implementation in a 68020 like environment probably requires that an
argument pointer or argument count be pushed as the last argument in
all function calls.  More exotic calling sequences might require
information about the type of each argument.

-- 
Daniel F. Fisher
dff@morgan.com

karl@haddock.ima.isc.com (Karl Heuer) (01/14/89)

In article <9358@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>In article <11410@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
>>... given this information, the code ... is portable.
>
>No it isn't!!  ... your use of sizeof to adjust the byte count is flawed,
>because sizeof doesn't take into account alignment requirements of the
>arguments, which can (and sometimes do) differ from those of array members.

I'm assuming that va_nbytes() would be *defined* to return sum(each undeclared
argument a)(sizeof(a)), rather than the amount of space used on the stack.  On
implementations where these agree, there may be a fast way to compute the
value from the callee.  On implementations with the strange alignment
requirements, or where other problems prevent the callee from doing this, the
compiler would have to provide the information at the caller's end.

Actually, both va_nbytes() and va_nargs() are more powerful than necessary.
Typically, all the user wants to know is whether or not there is another
argument available for fetching with va_arg(), so a boolean va_isarg() would
suffice.  Any implementation on which it is currently possible to write either
va_nbytes() or va_nargs() correctly could add va_isarg() in such a way that
there is no cost associated with it in programs that don't use it.  (There are
implementations in which none of these three can be written without passing
hints from the caller, which is probably the main reason why this isn't worth
standardizing.)

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

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/15/89)

In article <202@mstan.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes:
>Since arrays cannot be passed by value in C, I fail to see how this
>is relevant.

I wasn't talking about array ARGUMENTS.  sizeof returns the size of an
object when used as a member of an array.  This is not necessarily the
same as the number of bytes needed when used as an argument, due to
possibly different alignment requirements in the two cases.  In fact,
some arguments in some implementations are passed in fast registers!

>What to do? What to do?

Not to worry about va_nargs().  It's not useful enough to be worth
the hassle.

dff@Morgan.COM (Daniel F. Fisher) (01/15/89)

In article <9381@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
>In article <202@mstan.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes:
>>Since arrays cannot be passed by value in C, I fail to see how this
>>is relevant.
>
>                                   . . . sizeof returns the size of an
>object when used as a member of an array. . . .

I see Mr. Gwyn's point, and I believe it is relevant.

>>What to do? What to do?
>
>Not to worry about va_nargs().  It's not useful enough to be worth
>the hassle.

That is the opinion I expressed in my earlier posting.  I indicated
that while a va_nargs(parg, type) macro is portable way for functions
to count their arguments, I felt that this would be wasteful, given
that other means were available, such as rewriting a function that
uses va_nargs() so that it takes its argument count as an argument.

My "What to do? What to do?" followed a discussion of how to implement
va_arg() in such away that when NDEBUG was not defined it would cause
the program to abort() if there were no remaining arguments of the
requested type.  What I couldn't figure out was how to implement this
facility so it did not beget the same overhead as that which is
inherent in va_nargs().  Specifically, I was wondering how to construct
a calling sequence that was ambivalent to the presence of argument count
information but which would allow the callee to ascertain the presence
of this information.

Perhaps a va_arg() macro that aborts if it does not have an argument
is also not worth the hassle.  Particularly if it requires the same
additional overhead in all function calls as va_nargs() appears to
require.  But I do believe it is MORE of a hassle than va_nargs().


In article <11428@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer)
writes on the same subject:

>Actually, both va_nbytes() and va_nargs() are more powerful than necessary.
>Typically, all the user wants to know is whether or not there is another
>argument available for fetching with va_arg(), so a boolean va_isarg() would
>suffice.

I considered suggesting va_isarg() when preparing my earlier posting,
but concluded that it is not appropriate, since the unavailability
of a necessary argument represents a programming error which is best
treated as an assertion failure.  This is what led me to suggest that
va_arg abort if it cannot return an argument of the requested type.
I can see, however, that a more general form of assert() might invoke
an exception handler, so I wish to modify my earlier descriptions of the
"bullet-proof" va_arg() to be "like the usual va_arg() except that,
prior to returning the argument, it asserts that it can and allows
assert() to take the usual action if the assertion fails."

-- 
Daniel F. Fisher
dff@morgan.com

karl@haddock.ima.isc.com (Karl Heuer) (01/27/89)

In article <203@s5.Morgan.COM> dff@Morgan.COM (Daniel F. Fisher) writes:
>In article <11428@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer)
>[suggests that for most purposes] a boolean va_isarg() would suffice.
>
>I considered [that], but concluded that it is not appropriate, since the
>unavailability of a necessary argument represents a programming error which
>is best treated as an assertion failure.

Here's a counterexample.
	int min(int first, ...) {
	    int i;  va_list ap;
	    va_start(ap, first);
	    while (va_isarg(ap)) {
	        if ((i = va_arg(ap, int)) < first) first = i;
	    }
	    return (first);
	}

This function, which cannot be written in either pre-ANSI or ANSI C, uses
va_isarg() to obviate the need for an explicit extra argument.

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

dyoung@media-lab.MEDIA.MIT.EDU (David Young) (02/12/91)

I'd like to be able to write a macro or a function that will take an
unspecified number of arguments -- similar to how the printf() function
works.  What I'd like is something that could transform a call like:

     PringMsg( window, formatString, <formatArgs>)

into the following chunk of code:

     { 
       sprintf( globalFoo, formatString, <formatArgs>);
       BlahBlah( window, globalFoo);
     }

I was hoping to find something similar to the &rest capability of Lisp.
But am scared I'll have to end up using vprintf().

Can anyone help or offer a suggestion or solution?

Thanks,

david young

jik@athena.mit.edu (Jonathan I. Kamens) (02/12/91)

  If you're using an ANSI C compiler that has <stdarg.h>, then see the
documentation in K&R 2 (and whatever books you have that talk about ANSI C)
that talks about variable-argument functions.

  If you're not, and your system has <varargs.h>, then see the man page for
varargs for information about how to do variable-argument functions.

  In fact, you probably *are* going to end up using vprintf rather than being
able to call sprintf and convince it that some of youre function's arguments
are the arguments that it should use.  But that isn't such a big deal if you
have vprintf.  If you don't there are public domain implementations of it all
over the place, including one in comp.sources.misc or comp.sources.unix (I
forget which).

  I've found that a good place to see how variable argument stuff works with
<varargs.h> is the source code for GNU awk.  

-- 
Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8085			      Home: 617-782-0710

henry@zoo.toronto.edu (Henry Spencer) (02/12/91)

In article <5196@media-lab.MEDIA.MIT.EDU> dyoung@media-lab.media.mit.edu.UUCP (David Young) writes:
>I'd like to be able to write a macro or a function that will take an
>unspecified number of arguments...

For a function, you'll need to use <stdarg.h> (ANSI C) or <varargs.h> (many
old implementations).  Can't be done with a macro at all.
-- 
"Read the OSI protocol specifications?  | Henry Spencer @ U of Toronto Zoology
I can't even *lift* them!"              |  henry@zoo.toronto.edu  utzoo!henry

sanders@peyote.cactus.org (Tony Sanders) (02/12/91)

In article <1991Feb11.225815.5875@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:
>For a function, you'll need to use <stdarg.h> (ANSI C) or <varargs.h> (many
>old implementations).  Can't be done with a macro at all.
This is annoying.  How do get around this problem so I can easily
eliminate debug code if NODEBUG is set (or if DEBUG isn't set, whichever).
For instance...

This works great:
#ifdef NODEBUG
#define DPV(var,type) /* Removes code like magic */
#else
#define DPV(var,type) fprintf(stderr,"%s:%d, " # var " = %" # type "\n",var);
#endif

But I can't do this:
#ifdef NODEBUG
#define DP(fmt,...) /* sigh */
#else
#define DP(fmt,...) fprintf(stderr,fmt,...);
#endif

So I do this:

#ifdef NODEBUG
#define DP(level) if (0)	/* I hope the optimizer gets rid of this */
#else
extern DebugPrint(char *,...);
#define DP(level) if (debug&level) DebugPrint
#endif
DP(1)("this fmt string %s\n","sucks rocks");


Any better ideas???

-- sanders@peyote.cactus.org
First rule of software:  Throw the first one away.
and so on...
I am not an IBM representative and I speak only for myself.

gerrit@xelion.UUCP (Gerrit Brouwer) (02/13/91)

sanders@peyote.cactus.org (Tony Sanders) writes:

>In article <1991Feb11.225815.5875@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:

>So I do this:

>#ifdef NODEBUG
>#define DP(level) if (0)	/* I hope the optimizer gets rid of this */
>#else
>extern DebugPrint(char *,...);
>#define DP(level) if (debug&level) DebugPrint
>#endif
>DP(1)("this fmt string %s\n","sucks rocks");


>Any better ideas???

I do this:

#ifdef	DO_DEBUG
#define	PRINT(ARGUMENTS) DebugPrint ARGUMENTS
#else
#define	PRINT(ARGUMENTS) DebugPrint ARGUMENTS
#endif	DO_DEBUG

PRINT(("any number of arguments allowed: %s %d %f\n", "string", 4, 3.14));


-- 
Gerrit Brouwer                     domain : gerrit@xelion.UUCP
XELION BV                          uucp   : uunet!mcsun!hp4nl!xelion!gerrit
Postbus 88                         phone  : +31 15 622121
2600 AB Delft, The Netherlands     fax    : +31 15 621760

dave@aspect.UUCP (Dave Corcoran) (02/14/91)

In article <5196@media-lab.MEDIA.MIT.EDU>, dyoung@media-lab.MEDIA.MIT.EDU (David Young) writes:
> 
> What I'd like is something that could transform a call like:
> 
>      PringMsg( window, formatString, <formatArgs>)
> 
> into the following chunk of code:
> 
>      { 
>        sprintf( globalFoo, formatString, <formatArgs>);
>        BlahBlah( window, globalFoo);
>      }
> 

run this through m4
--------------------8<--------------------------
define(PringMsg,`
     { 
       sprintf(globalFoo, $2, shift(shift($*)));
       BlahBlah($1, globalFoo);
     }')

PringMsg( window, formatString, f,o,rm,at,Ar,gs)
PringMsg( window, formatString, form,at,Args)
--------------------8<--------------------------

CAVEAT: you cannot have commas in formatString
-- 
David Corcoran		      -@@
uunet!aspect!dave	        ~
In a society where anything goes eventually everything will.

dave@aspect.UUCP (Dave Corcoran) (02/14/91)

In article <5270@awdprime.UUCP>, sanders@peyote.cactus.org (Tony Sanders) writes:
> But I can't do this:
> #ifdef NODEBUG
> #define DP(fmt,...) /* sigh */
> #else
> #define DP(fmt,...) fprintf(stderr,fmt,...);
> #endif
> 
> Any better ideas???

yet another posible m4 solution:
------------------8<----------------------------
define(DBP,`ifdef(`DEBUG',fprintf(stderr,$*))')

DBP("not printed %d",1); 
                      ^ unfortunately this "gets through" in both cases
define(`DEBUG')

DBP("printed %d%d%d",1,23,4);
DBP("so is this %s\n","string");
------------------8<----------------------------
-- 
David Corcoran		      -@@
uunet!aspect!dave	        ~
In a society where anything goes eventually everything will.

torek@elf.ee.lbl.gov (Chris Torek) (02/14/91)

In article <5196@media-lab.MEDIA.MIT.EDU> dyoung@media-lab.media.mit.edu.UUCP
(David Young) writes:
>What I'd like is something that could transform a call like:
>
>     PringMsg( window, formatString, <formatArgs>)
>
>into the following chunk of code:
>
>     { 
>       sprintf( globalFoo, formatString, <formatArgs>);
>       BlahBlah( window, globalFoo);
>     }

The following is the only current approach that is anywhere near
portable:

	#define SIZE	1024	/* and pray */
	#if __STDC__
	void
	PrintMsg(WINDOW *window, char *fmt, ...) {
		va_list ap;
		char buf[SIZE];

		va_start(ap, fmt);
		(void) vsprintf(buf, fmt, ap);
		va_end(ap);
		BlahBlah(window, buf);
	}
	#else
	void
	PrintMsg(va_alist)
		va_dcl
	{
		WINDOW *window;
		char *fmt;
		va_list ap;
		char buf[SIZE];

		va_start(ap);
		window = va_arg(ap, WINDOW *);
		fmt = va_arg(ap, char *);
		(void) vsprintf(buf, fmt, ap);
		va_end(ap);
		BlahBlah(window, buf);
	}
	#endif

(Something very much like this, but with a size of 2048, appears in
the X11 sources.)

This method is not terribly satisfactory.  The size sets an upper bound
on the amount that can be printed in one call.  Worse, if the format
plus arguments produce more than SIZE-1 characters, vsprintf silently
overruns the buffer, typically causing some kind of catastrophic error
soon afterward.

The latest Berkeley system (i.e., the one you cannot get yet) has two
new facilities in the C library that improve on this.  The first is the
pair of functions `snprintf' and `vsnprintf', which take a buffer size.
They return the number of characters required to hold the entire output.
Thus:

	/* declarations and beginning as before, but add `char *cp;'
	   and `int ret;' */
	ret = vsnprintf(buf, sizeof buf, fmt, ap);
	if (ret < sizeof buf) {
		/* everything was printed; the buffer is fine */
		cp = buf;
		goto done;	/* XXX `goto' is required on Pyramid */
	}
	/* some of the text was truncated */
	va_end(ap);		/* this macro may include an unbalanced } */
	cp = malloc(ret + 1);
	if (cp == NULL)
		die("out of memory");
	va_start(ap);
	window = va_arg(ap, WINDOW *);
	fmt = va_arg(ap, char *);
	(void) vsnprintf(cp, ret + 1, fmt, ap);
done:
	va_end(ap);
	BlahBlah(cp);
	if (cp != buf)
		free(cp);


The second facility, and the one that is preferred for this sort of
thing, is the ability to open your own I/O functions.  In this case the
desired function is the equivalent of the `write' system call, and it
needs one pointer parameter, which happens to be exactly what the
interface provides (in the shape of a `void *'):

	/* __STDC__ assumed here */
	static int
	aux_write(void *cookie, const char *buf, int nbytes) {
		WINDOW *w = cookie;

		window_write(w, buf, nbytes);
		return nbytes;
	}

	int
	PrintMsg(WINDOW *w, const char *fmt, ...) {
		FILE *fp;
		va_list ap;

		fp = fwopen(w, aux_write);
		if (fp == NULL)
			die("out of memory, or something equally bad");
		va_start(ap, fmt);
		(void) vfprintf(fp, fmt, ap);
		va_end(ap);
		(void) fclose(fp);
	}

This allows an arbitrarily large amount of data to move between the
caller and the window, passing through an arbitrarily small pipe
(the stdio buffer attached to the file `fp').

The only requirement here is that user I/O functions act like read()
and write() (and, if seek and close are provided, lseek() and close()).
-- 
In-Real-Life: Chris Torek, Lawrence Berkeley Lab EE div (+1 415 486 5427)
Berkeley, CA		Domain:	torek@ee.lbl.gov

catfood@NCoast.ORG (Mark W. Schumann) (02/17/91)

In article <5196@media-lab.MEDIA.MIT.EDU> dyoung@media-lab.media.mit.edu.UUCP (David Young) writes:
>I'd like to be able to write a macro or a function that will take an
>unspecified number of arguments -- similar to how the printf() function
>works.  What I'd like is something that could transform a call like:
>
>     PringMsg( window, formatString, <formatArgs>)
>
>into the following chunk of code:
>
>     { 
>       sprintf( globalFoo, formatString, <formatArgs>);
>       BlahBlah( window, globalFoo);
>     }
>
>I was hoping to find something similar to the &rest capability of Lisp.
>But am scared I'll have to end up using vprintf().
>
>Can anyone help or offer a suggestion or solution?

vsprintf() is part of ANSI C according to my Turbo manual.
^^^^^^^^


-- 
============================================================
Mark W. Schumann  3111 Mapledale Avenue, Cleveland 44109 USA
Domain: catfood@ncoast.org
UUCP:   ...!mailrus!usenet.ins.cwru.edu!ncoast!catfood