gnu@hoptoad.uucp (John Gilmore) (04/06/91)
In an ANSI C implementation, it appears that the type "va_list" must be defined by <stdio.h> because it it used to declare the arguments for vfprintf, vprintf, and vsprintf. However, it is not in the list of types declared by <stdio.h>, either in Appendix C.10 (page 192) or in section 4.9.1 (page 125). The sections that document these particular vXXX functions say, e.g.: #include <stdarg.h> #include <stdio.h> int vsprintf(char *s, const char *format, va_list arg); but it is not possible for <stdio.h> to declare these functions without also declaring va_list, since some people will include <stdio.h> without including <stdarg.h>. <stdio.h> could avoid declaring these functions if <stdarg.h> was not also included, but the includes could occur in either order, making things very messy, and I don't think that's what the standards committee had in mind. Is this a real omission, or am I missing something? -- John Gilmore {sun,uunet,pyramid}!hoptoad!gnu gnu@toad.com gnu@cygnus.com * Truth : the most deadly weapon ever discovered by humanity. Capable of * * destroying entire perceptual sets, cultures, and realities. Outlawed by * * all governments everywhere. Possession is normally punishable by death. * * ..{amdahl|decwrl|octopus|pyramid|ucbvax}!avsd!childers@tycho *
torek@elf.ee.lbl.gov (Chris Torek) (04/06/91)
In article <16863@hoptoad.uucp> gnu@hoptoad.uucp (John Gilmore) writes: >In an ANSI C implementation, it appears that the type "va_list" must be >defined by <stdio.h> ... No; in fact, the type `va_list' must not be defined there, but must be *used* there regardless (as you note). This leaves the problem of somehow using a type without first defining it. There is a way, based on the observation that `typedef' does not define a new type, but rather defines a new name for an existing type. What one does, then, is this: A. Define some underlying representation V for `va_list'. This is machine dependent, but is typically something like `pointer to char' or `pointer to struct __va_list'. B. In <stdio.h>, declare v*printf with something like: int vprintf(const char *fmt, struct __va_list *); C. in <stdarg.h>, define the type `va_list' with something like typedef struct __va_list *va_list; This introduces two new problems: <stdio.h> is now machine dependent (an otherwise-unnecessary situation), and <stdarg.h> and <stdio.h> must somehow be kept in sync. What we have done in the current BSD system, which solves all of this, is create a machine-dependent header file (found in <machine/ansi.h>, which is a minor misnomer) that reads more or less as follows: /* faux MIPS <machine/ansi.h> */ #ifndef _ANSI_H_ #define _ANSI_H_ struct __va_list { int n0; char *p0, *p1; }; /* 2 save areas */ #define _CLOCK_T_ unsigned long #define _PTRDIFF_T_ int #define _SIZE_T_ unsigned int #define _TIME_T_ unsigned int #define _VA_LIST_ struct __va_list * #define _WCHAR_T_ unsigned short #endif /* _ANSI_H_ */ This file is included whenever any of the machine-dependent types is required. Typedefs that appear in more than one standard header, such as size_t (which appears in <stddef.h>, <stdio.h>, <string.h>, and <time.h>), are defined in each standard header with the sequence #ifdef _SIZE_T_ typedef _SIZE_T_ size_t; #undef _SIZE_T_ #endif so that each such typedef appears at most once. Typedefs that appear in only one header (namely va_list) are simply defined via typedef _VA_LIST_ va_list; and <stdio.h> just uses the sequence #include <machine/ansi.h> int vprintf(const char *, _VA_LIST_); (had va_list to appear in more than one standard header, we would have had to create two implementation-space macro names for it; fortunately this is not the case). This `backwards' sequence (of defining the types via macros, then turning those into typedefs exactly once by undefining the macro in the process) uses the minimal amount of verbiage. Another scheme is to have one macro for each type, and another for each `typedef has been done' flag, but this is unnecessary. Machine-dependent headers such as <stdarg.h> are in fact read from the `machine' directory as well. Note that the technique used in SunOS 4.1 and 4.1.1, in which <sys/stdtypes.h> contains all the `typedef's for all standard headers, is incorrect, precisely because of the problem addressed here (some headers must use some types which those same headers must not define). (Good grief, these one-sentence paragraphs are starting to look like Bill-Joy-ese. :-) I find all the passive voice annoying too. [Look what happens when you get wheedled into editing man pages....]) -- In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427) Berkeley, CA Domain: torek@ee.lbl.gov
gwyn@smoke.brl.mil (Doug Gwyn) (04/06/91)
In article <16863@hoptoad.uucp> gnu@hoptoad.uucp (John Gilmore) writes: >In an ANSI C implementation, it appears that the type "va_list" must be >defined by <stdio.h> ... NO! To the contrary, a conforming implementation must NOT define va_list as a side effect of inclusion of <stdio.h>. >but it is not possible for <stdio.h> to declare these functions without >also declaring va_list, ... False.
gnu@hoptoad.uucp (John Gilmore) (04/10/91)
Thanks, Chris, for your useful response. My confusion lie in the fact that __VA_LIST__ is not #undef'd after use in <stdarg.h>, like all the other names defined in <machine/ansi.h>. I had a case where the user had included #include <stdarg.h> #include <stdio.h> and the compile was failing because __VA_LIST__ was undefined in stdio.h. Either the conventions for the use of each name in <machine/ansi.h> should be documented, or this unusual usage of __VA_LIST__ should be changed to match all the rest, and perhaps a new __VA_LIST_STICKY__ introduced for <stdio.h> to depend on. torek@elf.ee.lbl.gov (Chris Torek) wrote: > This introduces two new problems: <stdio.h> is now machine dependent > (an otherwise-unnecessary situation), and <stdarg.h> and <stdio.h> must > somehow be kept in sync. I believe that ANSI C should not have forced us to introduce these two new problems. In other words, this is a botch in the standard, that should be fixed in its next revision. Preferably the fix will simply permit <stdio.h> to define va_list, or indeed, for it to include <stdarg.h>, so that no backdoor monkeyshines at all would be needed. -- John Gilmore {sun,uunet,pyramid}!hoptoad!gnu gnu@toad.com gnu@cygnus.com * Truth : the most deadly weapon ever discovered by humanity. Capable of * * destroying entire perceptual sets, cultures, and realities. Outlawed by * * all governments everywhere. Possession is normally punishable by death. * * ..{amdahl|decwrl|octopus|pyramid|ucbvax}!avsd!childers@tycho *
gwyn@smoke.brl.mil (Doug Gwyn) (04/10/91)
In article <16965@hoptoad.uucp> gnu@hoptoad.uucp (John Gilmore) writes: >I believe that ANSI C should not have forced us to introduce these two >new problems. In other words, this is a botch in the standard, that >should be fixed in its next revision. Preferably the fix will simply >permit <stdio.h> to define va_list, or indeed, for it to include ><stdarg.h>, so that no backdoor monkeyshines at all would be needed. Many of the top C experts in the world disagreed with you. I think the standard has this exactly right. However, it does look like 4.4BSD has an awkward implementation. That's not the fault of the standard.
bhoughto@bishop.intel.com (Blair P. Houghton) (04/11/91)
In article <15781@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes: >Many of the top C experts in the world disagreed with you. >I think the standard has this exactly right. Is there a good reason why v*printf() aren't declared in <stdarg.h>? The only one I can think of is an issue of categorization that seems to have overwhelmed one of usage. How could you use v*printf() without <stdarg.h>? --Blair "I'm still avoiding varargs by proper top-down design..."
torek@elf.ee.lbl.gov (Chris Torek) (04/11/91)
>In article <16965@hoptoad.uucp> gnu@hoptoad.uucp (John Gilmore) writes: >>I believe that ANSI C should not have forced us to introduce these two >>new problems. In other words, this is a botch in the standard, that >>should be fixed in its next revision. Preferably the fix will simply >>permit <stdio.h> to define va_list, or indeed, for it to include >><stdarg.h>, so that no backdoor monkeyshines at all would be needed. In article <15781@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes: >Many of the top C experts in the world disagreed with you. >I think the standard has this exactly right. The standard tries to make things easy for users, and makes things hard for implementors in the process. Whether this is a Good Thing depends on whether you are a user or an implementor (and on whether you can get your job done using only the facilities provided in X3.159-1989, or whether you must use additional types, object, and functions). >However, it does look like 4.4BSD has an awkward implementation. >That's not the fault of the standard. Indeed? And how do *you* propose to implement <stdio.h> such that it is machine independent, yet meets all the constraints imposed by both ANSI and POSIX? (If you want to make stdio.h machine dependent, my reply is unprintable.) Remember that the goal is to have 4.4BSD run on: vax tahoe hp9000/300 (68020/030) intel 80386 (in as little as 640k!) mips (both big- and little-endian, i.e., decstations too) sparc and anything else we can trick anyone into doing :-) (HP-PA and 88000 would be nice). All of these are to be built from a single source. We are not there yet (only the first four have ever been integrated together in one place, and some of the systems are not yet running), but even four disparate platforms is tricky. (Note that when I say `from the same source', I mean it: you can NFS-mount /usr/src read-only, log on to all the different machine types, and run `cd /usr/src; make' on all of them simultaneously. Then you can take bets on which one will finish first....) (The binaries are put in per-machine directories in /usr/obj, via symlinks.) -- In-Real-Life: Chris Torek, Lawrence Berkeley Lab CSE/EE (+1 415 486 5427) Berkeley, CA Domain: torek@ee.lbl.gov
steve@taumet.com (Stephen Clamage) (04/12/91)
bhoughto@bishop.intel.com (Blair P. Houghton) writes: >In article <15781@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes: >>Many of the top C experts in the world disagreed with you. >>I think the standard has this exactly right. >Is there a good reason why v*printf() aren't declared in <stdarg.h>? >The only one I can think of is an issue of categorization >that seems to have overwhelmed one of usage. >How could you use v*printf() without <stdarg.h>? v*printf() aren't declared in <stdarg.h> because you would need to declare FILE in it, and this would blow the modularity -- you would have a type and functions declared whenever you used stdargs, even if you did not want standard I/O. Remember, if you do not include <stdio.h> FILE and v*printf are not reserved for use as local identifiers. You cannot USE v*printf() without including <stdarg.h>, but you can DECLARE them. Thus: 1. You may include <stdio.h> without <stdarg.h> if you do not need v*printf(). 2. You may include <stdarg.h> without <stdio.h> if you do not need standard I/O. 3. You must include both headers if you want to use v*printf(). This seems like entirely reasonable modularity to me. You pay for what you want to use. -- Steve Clamage, TauMetric Corp, steve@taumet.com
gwyn@smoke.brl.mil (Doug Gwyn) (04/16/91)
In article <11982@dog.ee.lbl.gov> torek@elf.ee.lbl.gov (Chris Torek) writes: >Indeed? And how do *you* propose to implement <stdio.h> such that it >is machine independent, yet meets all the constraints imposed by both >ANSI and POSIX? The obvious solution is: /* stdio.h: */ #include <sys/config.h> /* defines __* types (only) */ extern int vprintf(const char *,__va_list); /* etc. */ /* stdarg.h: */ #include <sys/config.h> /* defines __* types (only) */ #define va_list __va_list /* etc. */ /* sys/config.h: */ #ifndef __CONFIG_H_INCLUDED #define __CONFIG_H_INCLUDED typedef char *__va_list; /* or whatever is required */ /* other typedefs here */ #endif I believe some implementors are using one such typedef-file per standard typedef, but I would suggest having just ONE header file that requires proper configuration when porting the implementation. (You could try to automatically tailor this configuration file, but I personally would prefer to edit it manually so that I could be sure of the choices being made.) This may not be too far from what your <machine.h> does. You need to be careful not to introduce spurious nonreserved identifiers into the standard headers, which is why I show only __* names in the system-configuration header.
gwyn@smoke.brl.mil (Doug Gwyn) (04/16/91)
In article <3769@inews.intel.com> bhoughto@bishop.intel.com (Blair P. Houghton) writes: >Is there a good reason why v*printf() aren't declared in <stdarg.h>? Yes, because that would have been counter to existing practice, whereby they are declared by <stdio.h>.