robert@gitpyr.UUCP (Robert Viduya) (11/14/84)
[]
This has probably been mentioned before, but I'd like to present a new
aspect of the old problem of variable arguments.
About a day ago, a program arrived at our site via net.sources. The
program was a wargame simulator known as 'ogre'. I pulled the program off
and compiled and attempted to run it. The program repeatedly died in two
routines: display() and display_xy(). For the sake of brevity, and since
the problems in both routines were identical, I'll only show display():
display(line, format, args)
int line;
char *format;
int args;
{
movecur(line, 0);
eeol();
_doprnt(format, &args, stdout);
}
The following are examples of how the author called display():
display(16, "%s infantry (%d/%d D%d M%d)", action,
unit[i].attack, unit[i].range,
unit[i].defend, unit[i].moves_left);
display(16, "left to place: %d armor, %d infantry%s",
armor_points, infantry_points,
(cp_set) ? "." : ", CP");
Note the author's unconventional use of _doprnt(). The program's exact
point of death was in _vaarg().
Obviously, the author expects function parameters to be passed as a
sequential list of words starting at some memory address. Well, our site
is running a Pyramid 90x, a RISC machine. The architecture of the machine
makes it more efficient to pass parameters in registers, due to the it's
abundance of them (at any moment, a function has access to 64 registers: 16
global (general), 16 local, 16 parameter, and 16 temporary). The author's
assumption about memory parameter lists wasn't valid and subsequently, the
program died trying to access the arguments.
I'm not defending the author of the program. He shouldn't have used
_doprnt() as it is an internal function of the standard C library and there
isn't any guarantee that it will be in some future revision of the OpSys.
However, if C had a _s_t_a_n_d_a_r_d method of supporting variable-length argument
lists, he probably wouldn't have resorted to using _doprnt().
Anyway, put this message down as another reason to standardize the
language, especially the intentionally vague areas like variable-argument
lists.
One more thing: I've already fixed the program to printf() instead of
_doprnt(). Don't bother sending me instructions on how to do so.
robert
--
Robert Viduya
Office of Computing Services
Georgia Institute of Technology, Atlanta GA 30332
Phone: (404) 894-4669
...!{akgua,allegra,amd,hplabs,ihnp4,masscomp,ut-ngp}!gatech!gitpyr!robert
...!{rlgvax,sb1,uf-cgrl,unmvax,ut-sally}!gatech!gitpyr!robert
mjs@alice.UUCP (M. J. Shannon, Jr.) (11/15/84)
But there is a standard method of dealing with variable argument lists. #include <varargs.h> The contents of the file differ from machine to machine, but the `entry points' are standard. As to why _doprnt() was used: that's pretty easy. There is no standard analog to printf which accepts a variable argument list *as an argument*. Marty Shannon UUCP: {alice,rabbit,research}!mjs (rabbit is soon to die. Does this mean alice is pregnant? Yup!) Phone: 201-582-3199
hansen@pegasus.UUCP (Tony L. Hansen) (11/16/84)
< As to why _doprnt() was used: that's pretty easy. There is no standard < analog to printf which accepts a variable argument list *as an argument*. Take a look at vprintf(3) in the System Vr2 manual. It is exactly what you're asking for. Now if only it had been available and documented sooner! (Vprintf also existed, undocumented, in System III, but not in System V.) Now if only BSD would have picked up on it when 4.2 came along instead of documenting _doprnt(3)! Now if .... Tony Hansen pegasus!hansen
henry@utzoo.UUCP (Henry Spencer) (11/16/84)
> But there is a standard method of dealing with variable argument lists. > #include <varargs.h> > The contents of the file differ from machine to machine, but the `entry > points' are standard. Unfortunately, the ability to implement <varargs.h> is, to some extent, machine-dependent. There are some machines where variable argument lists are very hard to do, and must be kludged in very machine-dependent ways. > As to why _doprnt() was used: that's pretty easy. There is no standard > analog to printf which accepts a variable argument list *as an argument*. There is, however, a portable mechanism to do this job: sprintf followed by passing the resulting string to whoever wants it. -- Henry Spencer @ U of Toronto Zoology {allegra,ihnp4,linus,decvax}!utzoo!henry
chris@umcp-cs.UUCP (Chris Torek) (11/17/84)
There are times when it is important to be able to get at the innards of printf (i.e., _doprnt or _printf or _foobar or whatever the XYZ software co. calls it), because the existing functions just don't Do What You Want. There is a wonderfully nearly portable way to do this, and that is the v*printf routines in System V. Sigh. Even those may not Do What You Want. Probably the most general way to do things would be to have a routine called func_printf, which takes a function $f$, an argument $caddr_t arg$, a format $fmt$, and a variable argument list a la printf and friends. Then the routine that actually implements printf() et al. could call $f$, giving the character to be printed and the extra $arg$. If $caddr_t$ is the generic pointer type, you can then squeeze all needed info into a structure and pass its address. Of course, such an implementation would be terribly inefficient for ordinary stdio file I/O... In the meantime, I've got #ifdef pyr's in my Emacs source. Sigh. -- (This line accidently left nonblank.) In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (301) 454-7690 UUCP: {seismo,allegra,brl-bmd}!umcp-cs!chris CSNet: chris@umcp-cs ARPA: chris@maryland
kpmartin@watmath.UUCP (Kevin Martin) (11/17/84)
>> But there is a standard method of dealing with variable argument lists. >> #include <varargs.h> >> The contents of the file differ from machine to machine, but the `entry >> points' are standard. > >Unfortunately, the ability to implement <varargs.h> is, to some extent, >machine-dependent. There are some machines where variable argument lists >are very hard to do, and must be kludged in very machine-dependent ways. As long as they can be kludged. That's why the definitions are hidden in an include file. That way, to the user, they don't appear machine-dependant. >> As to why _doprnt() was used: that's pretty easy. There is no standard >> analog to printf which accepts a variable argument list *as an argument*. > >There is, however, a portable mechanism to do this job: sprintf followed >by passing the resulting string to whoever wants it. > Henry Spencer @ U of Toronto Zoology How about another addition to <varargs.h>, which, given a function, its return type, and one of the (partially-used) va_list's, passes the remaining arguments referred to by the va_list to the function? retval = va_apply( printf, int, nextarg ); Obviously, va_apply is a macro, but it usually needs a library routine written in (shudder) assembler to copy and build another argument list. Unfortunately, you still can't call fprintf unless the arglist YOU are given also contained the file pointer. va_apply has no way of passing args which *aren't* in the given arglist. Maybe a more general way of building an arglist to give to va_apply is required... (Yes, we have implemented va_apply, and I have used it) Kevin Martin, UofW Software Development Group
gam@amdahl.UUCP (Gordon A. Moffett) (11/18/84)
Robert Viduya (gatech!gitpyr!robert) writes: > I'm not defending the author of the program. He shouldn't have used > _doprnt() [ for variable-length arglists ] as it is an internal > function of the standard C library and there > isn't any guarantee that it will be in some future revision of the OpSys. > However, if C had a standard method of supporting variable-length argument > lists, he probably wouldn't have resorted to using _doprnt(). > > Anyway, put this message down as another reason to standardize the > language, especially the intentionally vague areas like variable-argument > lists. But is it necessary to tie this into the language? Both System V and BSD have varargs.h, does this not provide portable variable-arglist functions? (System V also provides a vprintf(3), which is used with varargs.h and works similarly to _doprnt()). -- Gordon A. Moffett ...!{ihnp4,hplabs,amd,nsc}!amdahl!gam 37 22'50" N / 122 59'12" W [ This is just me talking. ]
henry@utzoo.UUCP (Henry Spencer) (11/19/84)
> >Unfortunately, the ability to implement <varargs.h> is, to some extent, > >machine-dependent. There are some machines where variable argument lists > >are very hard to do, and must be kludged in very machine-dependent ways. > > As long as they can be kludged. > That's why the definitions are hidden in an include file. That way, to > the user, they don't appear machine-dependant. You miss my point. I didn't say that the implementation of <varargs.h> was machine-dependent; that's obvious. I said that the *ability* to implement <varargs.h> is machine-dependent. There are machines on which that user interface to variable-length argument lists simply cannot be implemented, period. > How about another addition to <varargs.h>, which, given a function, > its return type, and one of the (partially-used) va_list's, passes > the remaining arguments referred to by the va_list to the function? I haven't studied this closely, but I suspect that the same comment may apply: the ability to do this is machine-dependent. A partially-used argument list does not necessarily look like a shorter complete argument list. Hey, people, we've been over and over this point here and in unix-wizards: THERE IS NO FULLY PORTABLE WAY TO DO VARIABLE-LENGTH ARGUMENT LISTS IN C!!! Rebuttals by mail, please, so I can tell you why your idea won't work without needing to thrash this out AGAIN in front of everyone. -- Henry Spencer @ U of Toronto Zoology {allegra,ihnp4,linus,decvax}!utzoo!henry
chris@umcp-cs.UUCP (Chris Torek) (11/20/84)
> > As to why _doprnt() was used: that's pretty easy. There is no standard > > analog to printf which accepts a variable argument list *as an argument*. > There is, however, a portable mechanism to do this job: sprintf followed > by passing the resulting string to whoever wants it. But this only works for (a large number of) specific cases. Suppose a string could be arbitrarily long? Then you need an arbitrarily long buffer. A function-based printf would seem to be the ``best'' way to be everything to everyone, but there is no such beast. Sigh. Granted, one should use sprintf when possible. But (as many have found out) sprintf is dangerous, since it will merrily overflow buffers and scribble on top of useful data. snprintf() and sxprintf() help, but only those who have implemented Patrick Powell's changes have those. Again, sigh. -- (This line accidently left nonblank.) In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (301) 454-7690 UUCP: {seismo,allegra,brl-bmd}!umcp-cs!chris CSNet: chris@umcp-cs ARPA: chris@maryland
katzung@ucsfcgl.UUCP (Brian Katzung) (11/22/84)
> But there is a standard method of dealing with variable argument lists. > #include <varargs.h> > The contents of the file differ from machine to machine, but the `entry points' > are standard. > > As to why _doprnt() was used: that's pretty easy. There is no standard > analog to printf which accepts a variable argument list *as an argument*. > > Marty Shannon > UUCP: {alice,rabbit,research}!mjs > (rabbit is soon to die. Does this mean alice is pregnant? Yup!) > Phone: 201-582-3199 (I didn't see the original article so I'm not sure what was said. My apologies if I'm repeating something.) The "standard method" can't always be applied. As far as I know, it is impossible to do under some implementations (I'd be delighted to have somebody prove me wrong by providing a non-trivial solution (ie, one that doesn't consist of separate entry points)). The problem arises on systems like ZILOG that put some arguments in registers (it may speed up execution a little, but it slows me down a lot). Fixing argument references in the case of printf-like routines is messy, but straight-forward: printf(rformat, rarg1, rarg2, rarg3, sarg1) /* r = in register, s = on stack */ char *rformat... { int regcnt = 3; char *argp = &rarg1; /* (argp <- addr of copy of reg) */ /* * Current argument is *argp. Next argument is: */ if (--regcnt) /* Not last register argument. */ (Advance pointer by appropriate amount) else /* Switch from r to s args. */ argp = &sarg1; } Now suppose I have a pair of functions, say "xlist" and "xvec", that have been written on another system and have been "sprinkled" into numerous modules. The routines have the following structure: xvec (vec) xtype *vec; /* ((xtype) 0) terminated vector. */ { body } xlist (element1) /* Called xlist(e1, e2, ..., en, (xtype) 0); */ xtype element1; { xvec(&element1); /* Let xvec do the work. */ } On a Zilog system, xvec has to be split into a routine that takes a vector, and a routine that takes the address of the first "register" argument and the address of the first stack argument. All occurrences of xlist-style calls to xvec have to be changed to xvec2(&rarg1, &sarg1), and xvec becomes "xvec2(vec, &vec[4])" (xvec2 now does the work). Brian Katzung ucbvax!ucsfcgl!katzung
thomas@utah-gr.UUCP (Spencer W. Thomas) (11/24/84)
In article <394@ucsfcgl.UUCP> katzung@ucsfcgl.UUCP (Brian Katzung) writes: >> But there is a standard method of dealing with variable argument lists. >> #include <varargs.h> >> The contents of the file differ from machine to machine, but the `entry points' >> are standard. >> >(I didn't see the original article so I'm not sure what was said. >My apologies if I'm repeating something.) > >The "standard method" can't always be applied. As far as I know, it >is impossible to do under some implementations (I'd be delighted to >have somebody prove me wrong by providing a non-trivial solution (ie, >one that doesn't consist of separate entry points)). > >The problem arises on systems like ZILOG that put some arguments in >registers (it may speed up execution a little, but it slows me >down a lot). The Pyramid is an example of a system that puts args in registers, but they were perfectly able to write a "varargs" package. The secret is a little subroutine that knows where arguments are to be found and snarfs up the first "n" of them from the registers and the rest from the stack (or wherever), and builds a contiguous memory array of them. Works fine, and their implementation of sprintf (using varargs) runs fine on their system, and on the Vax (the two places I've tried it). I think that with sufficient "cleverness", one can always get away with this type of trick. =Spencer
henry@utzoo.UUCP (Henry Spencer) (11/25/84)
> ... The secret is a little subroutine that knows where arguments are to be > found and ... builds a contiguous memory array of them. ... I think > that with sufficient "cleverness", one can always get away with this > type of trick. Does the magic routine parse the printf string to find out how many arguments there are? If not, how does it determine this? Just stuffing the register arguments "onto the front" of the in-memory arglist is a machine-dependent trick that won't always work. And always copying "a safe number" of in-memory arguments doesn't work, because it assumes that the arguments are ascending in memory, and that copying extra bytes won't cause a memory fault or whatever. In addition, there are some misguided "high-level-language" machines -- the PERQ is an example -- which have a related problem that this trick doesn't solve. Such machines *insist* on knowing how big the arglist is for a given function, have it figuring in the calling sequence to the point where you can't lie without disaster, and insist that the number be a constant for each function. And no, the PERQ doesn't (or at least didn't, last I heard) have a JSR instruction you could use to build your own calling sequence. You get to choose between inefficient argument passing for all functions (always build an arglist in memory and pass a pointer to it, rather than passing the args directly), or forgetting about <varargs.h> and kludging printf et al in machine-dependent ways. -- Henry Spencer @ U of Toronto Zoology {allegra,ihnp4,linus,decvax}!utzoo!henry
hamilton@uiucuxc.UUCP (12/12/84)
>> ... The secret is a little subroutine that knows where arguments are to be >> found and ... builds a contiguous memory array of them. ... I think >> that with sufficient "cleverness", one can always get away with this >> type of trick. > >Does the magic routine parse the printf string to find out how many >arguments there are? If not, how does it determine this? i think the "secret" subroutine he's referring to is _vaarg(), which gets called by the va_arg macro in <varargs.h>. however, it doesn't really build up a list; since it's called in a sequential-access mode, it only scans a pointer across the places where args are stashed. it's main job is figuring when that pointer needs to jump from the register frame to the stack. other lovers of _doprnt() who have migrated to a pyramid 90x, try this: #include <stdio.h> main () { foo ("%d %.0f %s\n", 1, 2., "3"); } foo (fmt, args) char *fmt; int args; { int varargs_block[3]; _doprnt (fmt, _vastart (varargs_block, &args), stdout); } wayne ({decvax,ucbvax}!pur-ee!uiucdcs!uiucuxc!)hamilton