dave@rosesun.Rosemount.COM (Dave Marquardt) (06/29/87)
A user came to me recently with the following problem, and since I've never
written anything using varargs, I'm stumped.
The user wants to pass a variable number of arguments to a function like
printf, like this:
#include <curses.h>
WINDOW *window1;
dprintf(a,b,s,va_alist)
int a,b;
char *s;
va_dcl
{
wmove(window1, a, b);
wprintw(window1, s, va_alist);
}
First off, can you do something like this? Secondly, I doubt that I have
the syntax right. Can someone point me in the right direction?
Dave
Dave Marquardt
dave@rosevax.Rosemount.COM
chris@mimsy.UUCP (Chris Torek) (07/01/87)
In article <1332@rosevax.Rosemount.COM> dave@rosesun.Rosemount.COM (Dave Marquardt) writes: >#include <curses.h> > >WINDOW *window1; > >dprintf(a,b,s,va_alist) >int a,b; >char *s; >va_dcl >{ > wmove(window1, a, b); > wprintw(window1, s, va_alist); >} > >First off, can you do something like this? No. >Secondly, I doubt that I have the syntax right. That is close enough. There are two problems. First, varargs is (as far as I can tell) only `kosher' when used for all the arguments. (The dpANS stdarg variable-argument-list mechanism works differently, and so I consider this a minor violation. Declaring a few fixed arguments seems to work in all the implementations I have used.) More important, though, is that wprintw takes a variable argument list, not a single argument of type `va_list'. System V has some of what you need in the form of `vprintf'. If curses had a wvprintw, you could do this: #include <curses.h> #include <varargs.h> WINDOW *window1; dprintf(va_alist) va_dcl { int a, b; char *fmt; va_list l; va_start(l); a = va_arg(l, int); b = va_arg(l, int); fmt = va_arg(l, char *); wmove(window1, a, b); wvprintw(window1, fmt, l); } `vprintf' takes a format and an argument of type `va_list'. vfprintf takes one additional file pointer, and vsprintf takes a string pointer. All return the number of characters printed. This is clearly the Right Way to allow this sort of thing; indeed, it suggests that printf, fprintf, and sprintf themselves be implemented using <varargs.h> (or <stdarg.h>), and hence portable. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) Domain: chris@mimsy.umd.edu Path: seismo!mimsy!chris
chris@mimsy.UUCP (Chris Torek) (07/01/87)
Oops: I left a `va_end(l)' out of the source in the parent article, <7268@mimsy.UUCP>. For a second example, here is the error() routine from the local C library. Note that it `cheats' by declaring three fixed arguments before the variable argument list. It also depends on crt0.o (the thing that calls main()) to set `_argv0' equal to main's argv[0]. This way all programs that call `error' print out their own names before printing their error messages. #include <stdio.h> #include <varargs.h> char *_argv0; /* argv[0], set by C startup code */ /* * error - University of Maryland specific (sigh) * * Useful for printing error messages. Will print the program name * and (optionally) the system error associated with the values in * <errno.h>. */ error(quit, e, fmt, va_alist) int quit; register int e; char *fmt; va_dcl { extern char *sys_errlist[]; extern int sys_nerr, errno; va_list l; register char *p = _argv0; if (e < 0) e = errno; (void) fflush(stdout); /* somewhat gratuitous */ if (p != NULL) (void) fprintf(stderr, "%s: ", p); /*** the next three lines are the interesting ones ***/ va_start(l); (void) vfprintf(stderr, fmt, l); va_end(l); /******/ if (e > 0) { if (e < sys_nerr) (void) fprintf(stderr, ": %s", sys_errlist[e]); else (void) fprintf(stderr, ": unknown error number %d", e); } (void) putc('\n', stderr); (void) fflush(stderr); if (quit) exit(quit); } -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) Domain: chris@mimsy.umd.edu Path: seismo!mimsy!chris
karl@haddock.UUCP (Karl Heuer) (07/02/87)
In article <7268@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: >In article <1332@rosevax.Rosemount.COM> dave@rosesun.Rosemount.COM >(Dave Marquardt) [asks how to write a private variadic function which passes >its va_alist to some public variadic function such as curses' wprintw]. > >No [you can't do that]. [The more important problem] is that wprintw takes a >variable argument list, not a single argument of type `va_list'. > >System V has some of what you need in the form of `vprintf'. If >curses had a wvprintw, you could [pass the va_alist to it]. Curses *should* have a wvprintw, or vwprintw as it would probably be called. (It has neither, at least not in the version I have here.) More generally, each truly variadic function% should have a corresponding v-function. I've never needed a vscanf, but that should exist too, just for completeness. >This is clearly the Right Way to allow this sort of thing; indeed, it >suggests that printf, fprintf, and sprintf themselves be implemented using ><varargs.h> (or <stdarg.h>), and hence portable. Some implementations do exactly that, so that vfprintf is called by each of the other 5 functions, which are written in C and (except for vsprintf using undocumented properties of FILE) are portable. The lowest-level routine, vfprintf, could be written in C but is often written in assembly for speed. Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint %"truly variadic function" is intended to exclude functions with a bounded number of optional arguments, of which "open" may or may not be an example.
gwyn@brl-smoke.ARPA (Doug Gwyn ) (07/04/87)
/* example implementation of printf() using varargs */
#include <varargs.h>
printf( va_alist ) /* params: char * followed by variable arguments */
va_dcl
{
va_list ap;
char *s; /* for the known parameter */
va_start( ap );
s = va_arg( ap, char * ); /* pick up first param */
vprintf( s, ap ); /* specially-designed function */
va_end( ap );
}
NOTE: the function being called (vprintf() here) MUST be designed to
accept a va_list parameter. Also, be very careful to balance va_start()
with va_end(); these macros may have { and } respectively embedded in them,
which affects their proper use in more complex situations.
karl@haddock.UUCP (Karl Heuer) (07/07/87)
In article <6048@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn) writes: >[example implementation of printf() using varargs] >... Also, be very careful to balance va_start() with va_end(); these macros >may have { and } respectively embedded in them, which affects their proper >use in more complex situations. I believe Andrew Koenig, the author of varargs, has stated that such implementations are incorrect; code such as va_start(ap); if (...) { ... va_end(ap); ... } else { ... va_end(ap); ... } is perfectly valid. Is this impossible to do right with some compilers? Note that ANSI's <stdarg.h>, which replaces varargs, does not mention a constraint of "syntactic balancing". Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
gwyn@brl-smoke.UUCP (07/07/87)
In article <685@haddock.UUCP> karl@haddock.ISC.COM (Karl Heuer) writes: >Note that ANSI's <stdarg.h>, which replaces varargs, does not mention a >constraint of "syntactic balancing". I recommended it for <varargs.h> based on experience. I don't know if it is provably necessary for some implementations, but since some of them (e.g. OSx) depend on it one might as well be careful ("defensive").
hansen@pegasus.UUCP (07/09/87)
The UNIX System V release 3 version of the curses library makes writing
functions such as this much easier by providing the vwprintw() function.
From the Vr3 curses man page:
vwprintw(win, fmt, varglist)
WINDOW *win;
char *fmt;
va_list varglist;
This routine corresponds to vfprintf(3S). It performs a
wprintw() using a variable argument list. The third
argument is a va_list, a pointer to a list of arguments,
as defined in <varargs.h>. See the vprintf(3S) and
varargs(5) manual pages for a detailed description on
how to use variable argument lists.
Your dprintf function now becomes:
#include <curses.h>
#include <varargs.h>
WINDOW *window1;
dprintf(va_alist)
va_dcl
{
int a,b;
char *s;
va_list ap;
va_start(ap);
a = va_arg(ap, int);
b = va_arg(ap, int);
s = va_arg(ap, char*);
wmove(window1, a, b);
vwprintw(window1, s, va_alist);
va_end(ap);
}
(Note how the arguments are pulled off the stack rather than being declared
as parameters. The way you had it is non-portable using <varargs.h>. See the
varargs man page for more info on using varargs.)
There is also a corresponding vwscanw() function for doing input.
If you don't have the System V release 3 version of curses, you should look
into using the vsprintf() function which has been mandated as part of the
dpANS C standard. Using this function, you can replace the above vwprintw()
statement with:
char buf[BUFSIZ];
vsprintf(buf, s, va_alist);
wprintw(window1, "%s", buf);
If you don't have vsprintf(), complain to your C vendor. There have been
several public domain versions posted in the past.
Tony Hansen
ihnp4!pegasus!hansen