[170: Tue Jan 19 01:24:16 1999 NEW STDARG GUIDELINES FOR THE INCREASINGLY TIRED OF FIGHTING: (0) (Don't even dream of using varargs.h) (1) Don't use stdarg. It's flaky and inconsistent across architectures and compiler writers seem to be losing psyche for even trying to get them to work. (2) Where stdarg routines are needed (e.g., type-extended printf abstractions and such), use stdargs very very gingerly: (2.1) Process all the varargs as soon as possible in the function, before calling any other functions with more than one or a couple small (fitting in registers) arguments. For example, don't do this: int f(int i,...) { va_list ap; g(1,2,3,what,are,we,fighting,for); .. va_start(ap,i); vhumbertprintf(i,ap); va_end(ap); } Instead, reorganize to scan the varargs immediately, even if you have to use temp store or extra copying: int f(int i,...) { va_list ap; va_start(ap,i); vhumbertprintf(i,ap); va_end(ap); .. g(1,2,3,what,are,we,fighting,for); .. } (2.2) Bracket the varargs processing as tightly as possible with va_start/va_end. (2.3) Never expect to be able to scan a given varargs list more than once, by any means. If you are tempted to -- e.g., to count arg sets before processing them -- don't. (Note that all attempts to scan a varargs list more than once will involve doing disallowed things with va_list's, see next point.) (3) Do not kid yourself into thinking you can treat 'va_list' as if it was a real type. In particular: (3.1) Don't assume a va_list passed as an argument is passed with `deep copy' value semantics. It may be, but it may not be. (So you couldn't do, say int foo(char *fmt,...) { void bar(char * fmt,va_list a1,va_list a2) va_list ap; { .. } va_start(ap,fmt); bar(fmt,ap,ap); } and expect to scan the two va_list args in bar separately.) (3.2) Similarly, DON'T assume that a va_list passed as an argument ISN'T passed with reference semantics. For example, you can't assume that in int f(int i,...) { void bar(va_list ap) va_list ap; { va_arg(ap,int); } va_start(ap,i); bar(ap); printf("%d",va_arg(ap,int)); } the printf will print the first variadic argument after the 'i' in 'f'. bar's use of va_arg(ap) MAY advance 'f's ap or it MAY not. (3.3) Don't imagine you can take the address of a va_list arg, EXCEPT within the function where the va_list is declared and the corresponding va_start occurs. For example, don't do this: int f(i,...) void g(va_list ap) void h(va_list *pa) { { { va_list ap; h(&ap); printf("%d",va_arg(*pa,int)); va_start(ap,i); } } g(ap); va_end(ap); } Instead, take the address immediately in f: int f(i,...) void g(va_list *pa) void h(va_list *pa) { { { va_list ap; h(pa); printf("%d",va_arg(*pa,int)); va_start(ap,i); } } g(&ap); va_end(ap); } (3.4) You probably shouldn't imagine that passing a va_list * guarantees you reference semantics either, even though it might. But even if it does, you'll be hard pressed to avoid violating (2.1) if you do. (4) Don't use stdarg. (5) If you do use stdarg, develop one or a few argument delimiting schemes that you will use systematically. Develop data structures that represent that/those argument schemes. Develop routines that read the various argument schemes and build an argument data structure. Make all your variadic functions look like: int f(char * woof,...) { va_list ap; BasicArgsType * b; va_start(ap,woof); b = stdargBasicArgs(woof,ap); va_end(ap); return fBasicArgs(b); } int fBasicArgs(BasicArgsType * b) { ..do the heavy lifing, use b info, etc.. ..discard b if necessary.. } Following this structure (5.1) Allows use of stdarg reasonably safely and portably (5.2) Standardizes and exposes the BasicArgs (etc) interfaces, so that (5.2.1) Argument sets can be developed at runtime, rather than only at compile time, and (5.2.2) The parsing cost of converting from variadic can be avoided if needed by going through the BasicArgs (etc) interface. :170]