Help with varargs

Chris Torek chris at mimsy.umd.edu
Wed Mar 14 01:52:09 AEST 1990


In article <136 at caslon.cs.arizona.edu> dave at 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 at cs.umd.edu	Path:	uunet!mimsy!chris



More information about the Comp.lang.c mailing list