[comp.lang.c] va_list used in <stdio.h>

dal@midgard.Midgard.MN.ORG (Dale Schumacher) (08/12/89)

I'm working on the header files for a PD LIBC implementation with the
goal that it be as close as possible to X3J11 and POSIX conformance.
I'm working from the May 13, 1988 draft of the C pANS.

In the <stdio.h> header file, the v[fs]printf() function prototypes
use the va_list type, but va_list is not defined anywhere in <stdio.h>.
The synopsis shows the need to #include both <stdarg.h> and <stdio.h>
in order to use v[fs]printf(), but what about #include'ing <stdio.h> in
a program which doesn't use these functions?  The prototype would then
be an error, right?  Also, section 4.1.2 (the v[fs]printf() functions
are in 4.9.6.[789]) states "Headers may be included in any order; ...".
In order for the declaration to work, <stdarg.h> must be #include'd first.
The standard also seems to imply that header files do not include each
other.  Is this the case?  Do I have an outdated draft of the standard?

gwyn@smoke.BRL.MIL (Doug Gwyn) (08/13/89)

In article <1140@midgard.Midgard.MN.ORG> dal@midgard.Midgard.MN.ORG (Dale Schumacher) writes:
>In the <stdio.h> header file, the v[fs]printf() function prototypes
>use the va_list type, but va_list is not defined anywhere in <stdio.h>.

This is a frequent question.  The answer is that the implementor of
<stdio.h> must not attempt to use "va_list" but instead should just
use the actual type expressed without use of typedefs.

>The standard also seems to imply that header files do not include each
>other.  Is this the case?

Yes, in effect each standard header contains exactly what the Standard
says it does and nothing else.  (Identifers specifically stated as
reserved for implementation use may also be defined/declared in standard
headers.  Warning!  Use __iob or _Iob, not _iob, in your <stdio.h>.)

bright@Data-IO.COM (Walter Bright) (08/15/89)

In article <1140@midgard.Midgard.MN.ORG> dal@midgard.Midgard.MN.ORG (Dale Schumacher) writes:
<I'm working on the header files for a PD LIBC implementation with the
<goal that it be as close as possible to X3J11 and POSIX conformance.
<I'm working from the May 13, 1988 draft of the C pANS.

Finally! Instead of wasting energy worrying about the GNU copyright,
you've got the right idea! Best of luck to you!

<In the <stdio.h> header file, the v[fs]printf() function prototypes
<use the va_list type, but va_list is not defined anywhere in <stdio.h>.

The trick to note is that <stdarg.h> is required for any *user* code that
uses variable argument lists. Since <stdio.h> is supplied by the *vendor*,
the problem can be solved in one of two ways,

1. In stdio.h, include the lines:
	#ifndef __STDARG_H		/* #define'd by stdarg.h	*/
	#include <stdarg.h>		/* get definition of va_list	*/
	#endif
   (As has been discussed here before, using 'wrappers' like this means
    that order dependent #include's can be #include'd in any order
    without a problem.)

2. As the compiler vendor, we *know* what va_list is, so we simply prototype
   vprintf and friends as:
	int vprintf(const char *,char *);
   instead of:
	int vprintf(const char *,va_list);

I use 2. because it compiles faster.

gwyn@smoke.BRL.MIL (Doug Gwyn) (08/15/89)

In article <2095@dataio.Data-IO.COM> bright@dataio.Data-IO.COM (Walter Bright) writes:
-1. In stdio.h, include the lines:
-	#ifndef __STDARG_H		/* #define'd by stdarg.h	*/
-	#include <stdarg.h>		/* get definition of va_list	*/
-	#endif

No, don't do this.  <stdio.h> is not allowed to define the va_* macros.

walter@hpclwjm.HP.COM (Walter Murray) (08/15/89)

Walter Bright writes:

> Since <stdio.h> is supplied by the *vendor*,
> the problem can be solved in one of two ways,

> 1. In stdio.h, include the lines:
> 	#ifndef __STDARG_H		/* #define'd by stdarg.h	*/
> 	#include <stdarg.h>		/* get definition of va_list	*/
> 	#endif

The problem with this is that it causes trouble for a program like
the following, which I think is perfectly legal.

   #include <stdio.h>
   #define va_start "My own personal macro"
   static char *va_list = va_start;
   main(){printf ("%s\n", va_list);}

Note that names like va_start and va_list are not reserved for
the implementation if the program has not explicitly included
any associated header.

Walter Murray
-------------

scs@adam.pika.mit.edu (Steve Summit) (08/17/89)

In article <1140@midgard.Midgard.MN.ORG> dal@midgard.Midgard.MN.ORG (Dale Schumacher) writes:
>In the <stdio.h> header file, the v[fs]printf() function prototypes
>use the va_list type, but va_list is not defined anywhere in <stdio.h>.

In article <10720@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>The answer is that the implementor of
><stdio.h> must not attempt to use "va_list" but instead should just
>use the actual type expressed without use of typedefs.

...which is difficult if the implementor of <stdio.h> is not also
the implementor of <stdarg.h>.

In article <2095@dataio.Data-IO.COM> bright@dataio.Data-IO.COM (Walter Bright) writes:
>1. In stdio.h, include the lines:
>	#ifndef __STDARG_H		/* #define'd by stdarg.h	*/
>	#include <stdarg.h>		/* get definition of va_list	*/
>	#endif

This is the right idea, although it assumes that <stdarg.h>
#defines __STDARG_H, which again an independently-written
<stdio.h> can't do.  (The #ifndef isn't really necessary;
<stdarg.h> can independently protect itself against multiple
#inclusion, albeit at the cost of an extra open, which some
people worry about.)  Anyway, it's illegal for a second reason:

In article <10739@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>No, don't do this.  <stdio.h> is not allowed to define the va_* macros.

If I were the implementer of both <stdio.h> and <stdarg.h>, I'd
shake my head at this conflicting set of requirements, take a
deep breath, and add something like va_list.h:

	#ifndef _VA_LIST
	#define _VA_LIST
	typedef int *__va_list;
	#endif

<stdarg.h> would then contain

	#include <va_list.h>
	typedef __va_list va_list;

and <stdio.h> would contain

	#include <va_list.h>
	extern int vfprintf(FILE *, char *, __va_list);

(I'd probably have to do the same thing for off_t, because
if fseek() doesn't take one, it should.  Schade, off_t is in
1003.1's baliwick, not X3J11's.)  This is, I'll admit, fussy,
but the rule against defining things in two places (so that an
eventual change must then be made in two places) is an extremely
good one, which I'll break only under extreme duress.

Besides the possible compilation speed degradation due to the
extra file-opening overhead, which Walter will remind me of if
somebody else doesn't, this solution is distasteful because the
prototype

	extern int vfprintf(FILE *, char *, __va_list);

is not "obviously" compatible with the call

	va_list argp;
	...
	vfprintf(fd, fmt, argp);

(Doug's suggestion to use the underlying va_list type directly
in the vfprintf prototype has the same difficulty).  "Obvious"
compatibility is desirable, among other reasons, because header
files containing prototypes are a convenient source of on-line
documentation.

Now, I know that a __va_list is really the same as a va_list,
and in fact the pANS guarantees it (typedefs are merely type
synonyms, not new types), but it still seems unfortunate, if only
because it's another nail in the coffin for that last sentence of
chapter 6 of K&R I:

	Finally, there is always the possibility that in the
	future the compiler or some other program such as lint
	may make use of the information contained in typedef
	declarations to perform some extra checking of a program.

Of course, the pANS typedef definition (which merely codifies
existing practice) has irreversibly sealed that coffin already,
and I doubt that anyone else remembers or cares about the quote
above anyway.

The correct vfprintf prototype in <stdio.h> is in any case only
barely workable for implementers of both <stdio.h> and <stdarg.h>
(i.e compiler and/or RTL vendors).  It is apparently impossible
to write a correct, standalone <stdio.h>, which is what Dale
Schumacher is trying to do.  (It happens that I am trying to do
the same thing, but had not worried about prototypes.  Alas!,
I have not posted my efforts yet, so Dale and I are duplicating
each other's work.)

Am I the only one who gets the same sort of feeling about
function prototypes as about symbolic links: a nice idea, but
introducing enough complications in practice that maybe they're
not so wonderful after all?  (Don't respond telling me what
prototypes are good for; I know what they're supposed to be good
for.)

Also in article <10720@smoke.BRL.MIL>, Doug writes:
>Warning!  Use __iob or _Iob, not _iob, in your <stdio.h>.

I'm not sure what is implied here.  It seems to me that _iob is
no more reserved that any other reserved identifier.  I'm
deliberately calling my buffer list _iob, to afford the
possibility of compatibility with previously-compiled object
files.  (Yes, this possibility is fraught with peril; and yes,
I like playing with fire.)  Using a different identifier might
conceivably avoid a conflict with a system-defined _iob (by
"system" I mean the complete C RTL that the purported standalone
stdio is attempting to augment), but there are enough other name
conflicts ("printf," etc.) that if any part of the "standard"
stdio gets linked in (_iob or otherwise) there's bound to be
trouble.  (I wish the pANS could drop the "p" sometime soon so
I could get a copy; Doug's point may just have to do with the
distinction between single and double leading underscores or
something.)

                                            Steve Summit
                                            scs@adam.pika.mit.edu

scs@adam.pika.mit.edu (Steve Summit) (08/17/89)

Last night I posted a long article exploring the impossibility
of writing a standalone stdio.h, that is, one that does not not
somehow "know" the underlying type of a va_list without
#including <stdarg.h>.  Doug Gwyn is about to post an article
saying that allowing standalone reimplementation of parts of the
C run-time library was never an X3J11 requirement, and I'm sure
that's true, but I don't have to like it.  In fact, there is a
way out of this particular dilemma, although it's too late now.

The essential problem is the requirement that <stdio.h> contain
prototypes for vprintf, vfprintf, and vsprintf.  The v*printf
family are "crossover functions:" they combine functionality from
the stdio and stdarg (nee varargs) subportions of the library.
In languages with more formal type and class concepts, explicit
means are often required to implement such crossover functions,
such as the "friend" function notation of C++.

A solution would have been to acknowledge the special nature of
the v*printf by putting them in their own header file, vprintf.h:

	#include <stdio.h>
	#include <stdarg.h>

	extern int vprintf(char *, va_list);
	extern int vfprintf(FILE *, char *, va_list);
	extern int vsprintf(char *, char *, va_list);

In general, I'd have preferred it if X3J11 had been somewhat more
granular in their assignment of new header files.  As another
example, it would be convenient if malloc, realloc, and free were
in a <malloc.h> rather than lumped in with everything else in
<stdlib.h>.  (This could make it easier for implementors of
debugging versions of malloc, intended to replace or sit atop
the vendor-supplied one.)

I realize that picking the right header file granularity involves
tradeoffs.  There are probably programmers who dislike having to
#include everything under the sun and would prefer an opposite
extreme, something along the lines of <libc.h> or <everything.h>.
Many of my source files start out with 15 or 20 #include
directives, and while this may not be ideal, I much prefer the
flexibility that finer granularity affords.

                                            Steve Summit
                                            scs@adam.pika.mit.edu

gwyn@smoke.BRL.MIL (Doug Gwyn) (08/17/89)

In article <13572@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes:
>...which is difficult if the implementor of <stdio.h> is not also
>the implementor of <stdarg.h>.

It is, reasonably I think, assumed that the Standard C implementation
on a system is provided as a single integrated whole, rather than
assembled piecemeal from several uncooperative sources.

>deep breath, and add something like va_list.h:
>	#ifndef _VA_LIST
>	#define _VA_LIST
>	typedef int *__va_list;
>	#endif
><stdarg.h> would then contain
>	#include <va_list.h>
>	typedef __va_list va_list;
>and <stdio.h> would contain
>	#include <va_list.h>
>	extern int vfprintf(FILE *, char *, __va_list);

That's one way to correctly implement these headers (except that
the "char*" parameter should be "const char*").

>(I'd probably have to do the same thing for off_t, because
>if fseek() doesn't take one, it should.  Schade, off_t is in
>1003.1's baliwick, not X3J11's.)

Also note that size_t and NULL are defined in more than one standard
header; NULL is not much of a problem because of benign redefinition,
but since size_t is a typedef it must be protected by some sort of
one-time interlock.

>It is apparently impossible to write a correct, standalone <stdio.h>,
>which is what Dale Schumacher is trying to do.

Sure it is, along the lines I suggested.  It just won't be fully
portable, that's all.  But nobody claimed that a fully portable
implementation of the standard C library was possible in the
first place.

Actually I don't think you are absolutely required to use prototypes
in the standard header function declarations, except for the ,...
functions (which vfprintf() is not).

>Also in article <10720@smoke.BRL.MIL>, Doug writes:
>>Warning!  Use __iob or _Iob, not _iob, in your <stdio.h>.
>I'm not sure what is implied here.

You should read Sue Meloy's article in the Journal of C Language
Translation.  Consider:

	#include <stdio.h>
	func() {
		int _iob;
		_iob = getchar();
	}

nfs@notecnirp.Princeton.EDU (Norbert Schlenker) (08/17/89)

In article <10739@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>In article <2095@dataio.Data-IO.COM> bright@dataio.Data-IO.COM (Walter Bright) writes:
>-1. In stdio.h, include the lines:
>-	#ifndef __STDARG_H		/* #define'd by stdarg.h	*/
>-	#include <stdarg.h>		/* get definition of va_list	*/
>-	#endif
>
>No, don't do this.  <stdio.h> is not allowed to define the va_* macros.

What is <stdio.h> allowed to define?  It has to pick up <stddef.h>, because
the prototypes in <stdio.h> use size_t.  What makes <stddef.h> allowable
and <stdarg.h> forbidden?

gwyn@smoke.BRL.MIL (Doug Gwyn) (08/18/89)

In article <18684@princeton.Princeton.EDU> nfs@notecnirp.UUCP (Norbert Schlenker) writes:
>What is <stdio.h> allowed to define?  It has to pick up <stddef.h>, because
>the prototypes in <stdio.h> use size_t.  What makes <stddef.h> allowable
>and <stdarg.h> forbidden?

<stdio.h> must not #include <stddef.h> either.

Each standard header is allowed to declare/define only those identifiers
specified in the Standard, plus others specifically reserved for
implementation use (e.g. "_Iobuf").

For POSIX purposes, #defining _POSIX_SOURCE before including a standard
header enables the declaration/definition of the additional identifiers
specified in IEEE Std. 1003.1.  Other such "feature macros" could be
used to further extend the names supplied by the standard headers, but
I'd recommend that separate headers be used instead.

datanguay@watmath.waterloo.edu (David Adrien Tanguay) (08/18/89)

Our compiler takes a different route to solve this problem. The parser knows
about all the standard types and functions. stdio.h #defines its macros
and tells the parser (with a #pragma) to turn on all the other stuff that
is defined in stdio.h. Basically, va_list is an internal type which is
aliased to the user visible name "va_list" when you #include <stdarg.h>.

David Tanguay

bill@twwells.com (T. William Wells) (08/18/89)

In article <18684@princeton.Princeton.EDU> nfs@notecnirp.UUCP (Norbert Schlenker) writes:
: What is <stdio.h> allowed to define?  It has to pick up <stddef.h>, because
: the prototypes in <stdio.h> use size_t.  What makes <stddef.h> allowable
: and <stdarg.h> forbidden?

Only and exactly what is in the standard. Any names that it defines
that are not specified in the standard must be names that are noted as
reserved.

No, it doesn't have to include <stddef.h>: all it has to do is define
size_t itself. Along with interlocks to prevent multiple definitions.

---
Bill                    { uunet | novavax | ankh | sunvice } !twwells!bill
bill@twwells.com

henry@utzoo.uucp (Henry Spencer) (08/19/89)

In article <13572@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes:
>>Warning!  Use __iob or _Iob, not _iob, in your <stdio.h>.
>
>I'm not sure what is implied here.  It seems to me that _iob is
>no more reserved that any other reserved identifier...
>... Doug's point may just have to do with the
>distinction between single and double leading underscores or
>something.)

Pretty much so.  __iob and _Iob are both in the name space that is defined
as available to the implementor and forbidden to the user.  _iob is in the
*user* name space and the implementation must not usurp it.
-- 
V7 /bin/mail source: 554 lines.|     Henry Spencer at U of Toronto Zoology
1989 X.400 specs: 2200+ pages. | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

henry@utzoo.uucp (Henry Spencer) (08/19/89)

In article <10766@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>You should read Sue Meloy's article in the Journal of C Language
>Translation...

How about a summary, Doug?  Many of us thought JCLT was interesting until
we saw the price tag, at which point we said "forget it".
-- 
V7 /bin/mail source: 554 lines.|     Henry Spencer at U of Toronto Zoology
1989 X.400 specs: 2200+ pages. | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

libes@cme.nbs.gov (Don Libes) (08/19/89)

In article <1989Aug18.184635.26773@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>In article <10766@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>>You should read Sue Meloy's article in the Journal of C Language
>>Translation...
>
>How about a summary, Doug?  Many of us thought JCLT was interesting until
>we saw the price tag, at which point we said "forget it".

I won't duplicate Doug's effort to post a summary of what was in the
first issue, but I will say that V1#1 was worthwhile.  In fact, I was
extremely impressed.

The articles were real "meat" articles spelling out problems and
solutions to the real hard problems facing C implementors and
programmers today.

About half of the essays were absolutely superb. (The rest were just
good.)  I was especially impressed with a historical article by
Plauger who always writes with incredible authority.  He answered many
questions that I had always wondered about.

It is likely that you may see this material if you are a member of any
of the C standards committees.  Some of it has appeared before in
those forums, although some of it is clearly new.  Nonetheless, it is
entirely different than, say, the C Users Journal (which I
occasionally write for, and which is aimed at the C programmer) or
this newsgroup (which has entirely too much chaff).  JCLT is aimed at
people designing C language tools, although it is certainly
interesting to other parties (like me).

As far as the price, I can't afford it either, but I am asking my
employer to buy a subscription.  (You'll notice this in your next tax
increase.)  If you are really as interested in C as you claim to be,
get your employer to buy a copy for crissakes.

Don Libes          libes@cme.nist.gov      ...!uunet!cme-durer!libes

Disclaimer: I have no connection whatsoever to JCLT.

gwyn@smoke.BRL.MIL (Doug Gwyn) (08/19/89)

In article <1989Aug18.184635.26773@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>How about a summary, Doug?  Many of us thought JCLT was interesting until
>we saw the price tag, at which point we said "forget it".

Me too.  However, if somebody is seriously trying to implement the C
standard, a subscription to the JCLT may quickly pay for itself.

I don't think I should attempt a summary of Sue's article; suffice it
to say that it explained how H-P decided to deal with name-space issues
in their ANSI C implementation.

bill@twwells.com (T. William Wells) (08/19/89)

In article <1989Aug18.184635.26773@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
: In article <10766@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
: >You should read Sue Meloy's article in the Journal of C Language
: >Translation...
:
: How about a summary, Doug?  Many of us thought JCLT was interesting until
: we saw the price tag, at which point we said "forget it".

Agreed.

---
Bill                    { uunet | novavax | ankh | sunvice } !twwells!bill
bill@twwells.com

henry@utzoo.uucp (Henry Spencer) (08/20/89)

In article <1515@muffin.cme.nbs.gov> libes@cme.nbs.gov (Don Libes) writes:
>... I will say that V1#1 was worthwhile.  In fact, I was
>extremely impressed.
>
>The articles were real "meat" articles spelling out problems and
>solutions to the real hard problems facing C implementors and
>programmers today.

Oh, I agree, the content is good stuff.  It's just ludicrously priced.

>As far as the price, I can't afford it either, but I am asking my
>employer to buy a subscription.  (You'll notice this in your next tax
>increase.)  If you are really as interested in C as you claim to be,
>get your employer to buy a copy for crissakes.

My employer would look at a PO for it, inquire "what's this?", and on
finding out that it was costing about C$2 per page, say "what in God's
name kind of paper is it printed on -- gold leaf?!?".  The upshot would
be "forget it".

It is a mistake to assume that C implementors are all rich corporations
to whom several hundred dollars a year is petty cash.
-- 
V7 /bin/mail source: 554 lines.|     Henry Spencer at U of Toronto Zoology
1989 X.400 specs: 2200+ pages. | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

diamond@csl.sony.co.jp (Norman Diamond) (08/22/89)

In article <13574@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes:

>I realize that picking the right header file granularity involves
>tradeoffs.  There are probably programmers who dislike having to
>#include everything under the sun and would prefer an opposite
>extreme, something along the lines of <libc.h> or <everything.h>.
>Many of my source files start out with 15 or 20 #include
>directives, and while this may not be ideal, I much prefer the
>flexibility that finer granularity affords.

As a quality-of-implementation issue, both needs could be met.
<stdlib.h> can just be a list of nested #include's of standard (well,
not really standard, but what else do you call them) include files
with a finer grain.  (At least namespace pollution is not a problem
in naming standard include files.)

--
-- 
Norman Diamond, Sony Computer Science Lab (diamond%csl.sony.jp@relay.cs.net)
  The above opinions are inherited by your machine's init process (pid 1),
  after being disowned and orphaned.  However, if you see this at Waterloo or
  Anterior, then their administrators must have approved of these opinions.