Path: utzoo!utgpu!news-server.csri.toronto.edu!bonnie.concordia.ca!uunet!cs.utexas.edu!sun-barr!lll-winken!ncis.tis.llnl.gov!dog.ee.lbl.gov!elf.ee.lbl.gov!torek From: torek@elf.ee.lbl.gov (Chris Torek) Newsgroups: comp.lang.c Subject: Re: Passing Variable Numbers of Arguments Message-ID: <9896@dog.ee.lbl.gov> Date: 14 Feb 91 11:07:53 GMT References: <5196@media-lab.MEDIA.MIT.EDU> Reply-To: torek@elf.ee.lbl.gov (Chris Torek) Organization: Lawrence Berkeley Laboratory, Berkeley Lines: 124 X-Local-Date: Thu, 14 Feb 91 03:07:53 PST In article <5196@media-lab.MEDIA.MIT.EDU> dyoung@media-lab.media.mit.edu.UUCP (David Young) writes: >What I'd like is something that could transform a call like: > > PringMsg( window, formatString, ) > >into the following chunk of code: > > { > sprintf( globalFoo, formatString, ); > 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@ee.lbl.gov