Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Help with varargs

0 views
Skip to first unread message

Chris Torek

unread,
Mar 13, 1990, 10:52:09 AM3/13/90
to
In article <1...@caslon.cs.arizona.edu> da...@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: ch...@cs.umd.edu Path: uunet!mimsy!chris

0 new messages