Passing Variable Numbers of Arguments
Chris Torek
torek at elf.ee.lbl.gov
Thu Feb 14 22:07:53 AEST 1991
In article <5196 at media-lab.MEDIA.MIT.EDU> dyoung at media-lab.media.mit.edu.UUCP
(David Young) writes:
>What I'd like is something that could transform a call like:
>
> PringMsg( window, formatString, <formatArgs>)
>
>into the following chunk of code:
>
> {
> sprintf( globalFoo, formatString, <formatArgs>);
> BlahBlah( window, globalFoo);
> }
The following is the only current approach that is anywhere near
portable:
#define SIZE 1024 /* and pray */
#if __STDC__
void
PrintMsg(WINDOW *window, char *fmt, ...) {
va_list ap;
char buf[SIZE];
va_start(ap, fmt);
(void) vsprintf(buf, fmt, ap);
va_end(ap);
BlahBlah(window, buf);
}
#else
void
PrintMsg(va_alist)
va_dcl
{
WINDOW *window;
char *fmt;
va_list ap;
char buf[SIZE];
va_start(ap);
window = va_arg(ap, WINDOW *);
fmt = va_arg(ap, char *);
(void) vsprintf(buf, fmt, ap);
va_end(ap);
BlahBlah(window, buf);
}
#endif
(Something very much like this, but with a size of 2048, appears in
the X11 sources.)
This method is not terribly satisfactory. The size sets an upper bound
on the amount that can be printed in one call. Worse, if the format
plus arguments produce more than SIZE-1 characters, vsprintf silently
overruns the buffer, typically causing some kind of catastrophic error
soon afterward.
The latest Berkeley system (i.e., the one you cannot get yet) has two
new facilities in the C library that improve on this. The first is the
pair of functions `snprintf' and `vsnprintf', which take a buffer size.
They return the number of characters required to hold the entire output.
Thus:
/* declarations and beginning as before, but add `char *cp;'
and `int ret;' */
ret = vsnprintf(buf, sizeof buf, fmt, ap);
if (ret < sizeof buf) {
/* everything was printed; the buffer is fine */
cp = buf;
goto done; /* XXX `goto' is required on Pyramid */
}
/* some of the text was truncated */
va_end(ap); /* this macro may include an unbalanced } */
cp = malloc(ret + 1);
if (cp == NULL)
die("out of memory");
va_start(ap);
window = va_arg(ap, WINDOW *);
fmt = va_arg(ap, char *);
(void) vsnprintf(cp, ret + 1, fmt, ap);
done:
va_end(ap);
BlahBlah(cp);
if (cp != buf)
free(cp);
The second facility, and the one that is preferred for this sort of
thing, is the ability to open your own I/O functions. In this case the
desired function is the equivalent of the `write' system call, and it
needs one pointer parameter, which happens to be exactly what the
interface provides (in the shape of a `void *'):
/* __STDC__ assumed here */
static int
aux_write(void *cookie, const char *buf, int nbytes) {
WINDOW *w = cookie;
window_write(w, buf, nbytes);
return nbytes;
}
int
PrintMsg(WINDOW *w, const char *fmt, ...) {
FILE *fp;
va_list ap;
fp = fwopen(w, aux_write);
if (fp == NULL)
die("out of memory, or something equally bad");
va_start(ap, fmt);
(void) vfprintf(fp, fmt, ap);
va_end(ap);
(void) fclose(fp);
}
This allows an arbitrarily large amount of data to move between the
caller and the window, passing through an arbitrarily small pipe
(the stdio buffer attached to the file `fp').
The only requirement here is that user I/O functions act like read()
and write() (and, if seek and close are provided, lseek() and close()).
--
In-Real-Life: Chris Torek, Lawrence Berkeley Lab EE div (+1 415 486 5427)
Berkeley, CA Domain: torek at ee.lbl.gov
More information about the Comp.lang.c
mailing list