Path: utzoo!attcan!uunet!mailrus!cs.utexas.edu!tut.cis.ohio-state.edu!purdue!haven!mimsy!chris From: chris@mimsy.umd.edu (Chris Torek) Newsgroups: comp.lang.c Subject: Re: Help with varargs Message-ID: <23061@mimsy.umd.edu> Date: 13 Mar 90 15:52:09 GMT References: <136@caslon.cs.arizona.edu> Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742 Lines: 99 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 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 */ } 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