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