[comp.lang.c] Help with varargs

dave@cs.arizona.edu (David P. Schaumann) (03/13/90)

I want to write a routine that uses variable number of args that passes
*all* of it's args to another routine.  I tried this:

	int s( size, va_alist )
	int size ;
	va_dcl

	{ t( size, va_alist ) ; }

	int t( size, va_alist )
	int size ;
	va_dcl

	{ int i ;
	  va_list ap ;

	  /* do some stuff with va_alist */

	}

Unfortunately, when I do this, only the first value in va_alist is right.
Everything else is garbage.  Anybody know how to fix this?

				Thanx, Dave
				dave@cs.arizona.edu

ping@cubmol.bio.columbia.edu (Shiping Zhang) (03/13/90)

In article <136@caslon.cs.arizona.edu> dave@cs.arizona.edu (David P. Schaumann) writes:
>I want to write a routine that uses variable number of args that passes
>*all* of it's args to another routine.  I tried this:
>
>	int s( size, va_alist )
>	int size ;
>	va_dcl
        ^^^^^^
This is the trick point.  I guess the types of your args are also varied
at different calls to this routine.  If I'm right, then you need using
union to deal with this problem. Just define an union with all possible
used data types in it. Then declare an array of this union, and put your
args in this array and use it as the args list.

 
-ping

chris@mimsy.umd.edu (Chris Torek) (03/13/90)

In article <136@caslon.cs.arizona.edu> dave@cs.arizona.edu
(David P. Schaumann) writes:
>I want to write a routine that uses variable number of args that passes
>*all* of it's args to another routine.

This cannot be done (portably).  That is, given a function that takes
the same arguments as, say, printf:

	#include <stdarg.h>

	int foo(char const *fmt, ...);

and an actual call:

	foo("%d %s %lu\n", 1, "2", 3LU);

and an implementation of foo:

	static int nfoo;	/* count of calls to foo */

	/* foo: just like printf, except that it counts total calls */

	int foo(char const *fmt, ...) {
		nfoo++;
		/* now we want to call printf() to actually do it */
		<??? what goes here ???>
	}

there is *nothing* you can put for the <???> line that will always
work, no matter how many arguments are passed to foo, etc.

There is, however, a solution.  While it is impossible to call printf(),
it is not impossible to achieve the same effect.  There are two ways
to do it: parse the format directly (within foo()), or---much simpler
---get in your DeLorean Time Machine, go back in time, and decree
that printf() also comes with vprintf().  I have done the latter for
you%, so instead of the <???> line, we can write:

	int foo(char const *fmt, ...) {
		int ret;
		va_list ap;

		nfoo++;			/* count another call to foo */
		va_start(ap, fmt);	/* get info on arguments */
		ret = vprintf(fmt, ap);	/* and then do a printf */
		va_end(ap);		/* clean up */
		return ret;
	}

Note that, to do this, we MUST have TWO versions of every `varargs'-
brand function: one that takes a literal variable argument list
(with a `...' prototype), and one that takes the `varargs info' thing
set up by va_start().  Indeed, the implementation of the `...' version
is simply a call to the `va_list' version---printf() can look exactly
like foo() above, minus the `nfoo++' line.

-----
% Just kidding.  Actually, my time machine is in the shop today,
  getting the brakes relined. :-)

  (Now I wonder if this will get the Bill Wolfe award for frivolity
  in exposition. :-) )
-----

For Classic C compilers, the only change is that the prototypes go
away and the function itself changes a bit:

	int
	foo(va_alist)
		va_dcl
	{
		char *fmt;
		int ret;
		va_list ap;

		va_start(ap);
		fmt = va_arg(ap, char *);
		ret = vprintf(fmt, ap);
		va_end(ap);
		return ret;
	}

Note that, to be Officially Correct, the `fmt = va_arg(ap, char *)' line
is necessary---you are Not Allowed to write

	int foo(fmt, va_alist) char *fmt; va_dcl { <...code...> }

(Although this happens to work on every machine I have used, and it makes
a better analogy to the new stdarg code, and I like it better myself and
wish that it *were* Offically Correct, it is not; avoid this temptation.
Write the ugly extra assigments.  Someday you will be glad you did.)

Summary: do not try to pass your arguments to another varargs function;
instead, pass your va_list object to a *different*, non-varargs function:
one that does what the varargs function does, but takes a va_list object.
If there is no such function, rewrite the code so that there is.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

dave@cs.arizona.edu (David P. Schaumann) (03/15/90)

Thanx to everybody who replied.  I now have >400 lines of replies via
email & news to sift through & try to (mentally) digest.  If you are
thinking of adding your 2 cents, please don't email ME.  I have as much
info on this as I need.  Anyone who wishes to take this up as a general
discussion is welcome to...

Again, thanx to all who sent replies.

Dave Schaumann
dave@cs.arizona.edu