created 1999
updated 2008-04-20T17:25:55
Under construction
Copyright © 1999 Gene Micheal Stover. All rights reserved.
At this time, this document is still in a "scrap of notes" or "notes from scraps" form.
It's source code that can compiler on pretty much any Unix system. The more systems, the better. Installation should be simple & seamless.
I like to check the state of an I/O stream (i.e., an instance of
istream, ostream, iostream,
or any of their subclasses) after I/O operations to ensure that
things worked. Sometimes this wasn't just detail-minded, it was
necessary. (Implementing a parser is such a case.) I generally
prefer explicit function calls to implicit type-casts or
constructors, & I also prefer to check for success, so I most
often used message good to ask a input stream if an
input operation succeeded. In other words, I did this:
int i;
istrstream strm ("123");
strm >> i;
if (strm.good ())
{
if (i == 123)
{
cout << "Good. The integer is " << i << "." << endl;
}
else
{
cout << "Failure." << endl;
}
This worked fine on Linux using gcc 2.8.1 &
libstdc++ 2.8.1.1, but it failed on HP/UX 10.20
aCC of unknown version. On HP/UX, the strm.good
() test failed. Interesting, that.
Some research revealed that, on Linux with gcc, the stream's
state after the input operation was "good"; in other words, no bits
were set in its state. On HP/UX, the stream's state was "eof" in
other words, the ios::eofbit bit was set.
Why the difference? Which implementation was correct?
If your compiler is Standard C, use the macros from
stdarg.h. Note that, in this set of macros,
va_start requires two arguments.
If your compiler is old C, use the macros from
varargs.h. Note that, in this set of macros,
va_start requires one argument. Also note that two
special symbols, va_alist & va_dcl,
are used when defining the function.
So do this:
#if __STDC__
# include <stdarg.h>
#else
# include <varargs.h>
#endif
#if __STDC__
int f00 (int i, ...);
#else
int foo (/* int i, ... */);
#endif
int
#if __STDC__
foo (int i, ...)
#else
foo (i, va_alist)
int i;
va_dcl
#endif
{
va_list va;
#if __STDC__
va_start (va, i);
#else
va_start (va);
#endif
...
..
va_end (va);
}
It's possible to write a set of macros that hide the different
versions of va_start, but it's impossible (?) to write
macros that hide the different function declarations. So I don't
think it's worth the effort of hiding the different
va_start macros.
I found this problem while porting some of my test tools at
Metapath to HP/UX. I had been using stdarg.h & the
macros in it. On HP/UX, when I compiled with the native compiler in
old C mode (which I needed for other portability reasons), I found
that the first time a function used the va_arg macro,
it retrieved the first argument. In other words, if I had used
"va_start (va, i)", then the first time I used
va_arg, my function retrieved the value of
i.
Much research \& thought, combined with attention to the
warnings from Standard C compilers that I was using
va_start in functions that don'thave variable argument
lists, revealed that the problem was in the va_...
macros. Using the appropriate set of macros for the compiler
(Standard or non-standard) fixes the problem.
I previously thought that stdarg.h differed from
varargs.h only to be a nuissance. This experience
revealed that the differences are necessary. (Or at least, they are
for a purpose.) I still don't know why the macros must match the
comiler. The best I figure is simply that the {\tt
__builtin_va_start} function is tailored to the compiler's mode
(Standard or non-standard). \section{Formal Argument Type
Promotions} On {\tt pdu4b.etl}, a Lynx machine with an old version
of the Gnu GCC compiler, the compiler spewed this error message:
{\tt \begin{poetry} test0063.c: In function S_Parent:
test0063.c:454: argument `childPid' doesn't match function
prototype test0063.c:454: a formal parameter type that promotes to
`int' test0063.c:454: can match only `int' in the prototype make:
*** [test0063] Error 1 \end{poetry} } The offending chunk of code
is this: {\tt \begin{poetry} static int S_Parent (p, childPid) int
p[2]; pid_t childPid; { ... } \end{poetry} }. The compiler is not
Standard C, so it did not compile the function prototype, which is
this: {\tt \begin{poetry} static int S_Parent (int p[2], pid_t
childPid); \end{poetry} }. Instead, it compiled a plain-old
function declaration. My first thought was that the Lynx GCC header
files did not define or {\tt typedef} ``pid\_t''. A grep showed
this was not so. The file {\tt types.h} contains these lines (among
other, of course): {\tt \begin{poetry} types.h: * Added mode_t,
nlink_t, and pid_t for POSIX compliance types.h:typedef short
pid_t; /* short so POSIX can use System V flock structure */
\end{poetry} } Hmmm\ldots {\tt pid\_t} is defined, in fact, it's
{\tt short}, \& the error message said something about types
that promote to {\tt int}. {\tt short} promotes to {\tt int}. So
that's the problem. Since the compiler doesn't know about function
prototypes, it has to use old-fashioned rules for promoting
function arguments. One of those rules is that a short
is promoted to {\tt int} before it's pushed on the stack as an
argument to a function. In such a system, no function can ever
receive a short as an actual argument. The compiler is
warning me about this. Changing the type of the childPid
argument from pid_t to int fixed the problem.
I see two important meanings from this:
End.