Utility to ensure appropriate headers were included

  • Thread starter Tomás Ó hÉilidhe
  • Start date
T

Tomás Ó hÉilidhe

Recently, there was a Linux program distributed as source code, and it
compiled fine on the majority of systems. However on some systems, it
failed to compile. On some of these systems, people were getting
errors for undeclared tokens, while others were getting linking
errors.

Anyway, the problem was that one of the source files was missing:

#include <stdio.h>

This wasn't a problem on most systems because some of the other header
files that were included actually included stdio.h.

Is there any utility out there that will process a source file and
notify you if you're depending on a declaration that isn't present in
a header file that's included directly in the source file?
 
J

jacob navia

Tomás Ó hÉilidhe said:
Recently, there was a Linux program distributed as source code, and it
compiled fine on the majority of systems. However on some systems, it
failed to compile. On some of these systems, people were getting
errors for undeclared tokens, while others were getting linking
errors.

Anyway, the problem was that one of the source files was missing:

#include <stdio.h>

This wasn't a problem on most systems because some of the other header
files that were included actually included stdio.h.

Is there any utility out there that will process a source file and
notify you if you're depending on a declaration that isn't present in
a header file that's included directly in the source file?

Yes, that utility is called a C compiler.
Normally, all compilers will warn you about missing declarations.
The problem is that people think that compiling means just
"press 'compile' in the IDE" and ignore completely the errors
or warnings that the compiler prints.
 
R

Richard Heathfield

jacob navia said:
Tomás Ó hÉilidhe wrote:

Yes, that utility is called a C compiler.

You appear to have mis-read the question.
Normally, all compilers will warn you about missing declarations.

He's not talking about missing declarations. He's talking about
declarations that depend on a header that is included indirectly by
another header.
The problem is that people think that compiling means just
"press 'compile' in the IDE" and ignore completely the errors
or warnings that the compiler prints.

No, that isn't the problem. It's *a* problem, but it's not *this* problem.
 
H

Harald van Dijk

Recently, there was a Linux program distributed as source code, and it
compiled fine on the majority of systems. However on some systems, it
failed to compile. On some of these systems, people were getting errors
for undeclared tokens, while others were getting linking errors.

Anyway, the problem was that one of the source files was missing:

#include <stdio.h>

This wasn't a problem on most systems because some of the other header
files that were included actually included stdio.h.

Is there any utility out there that will process a source file and
notify you if you're depending on a declaration that isn't present in a
header file that's included directly in the source file?

What exactly do you mean?

a.c:
#include <stdio.h>
int myputchar(char c) { return putchar(c); }

b.c:
#include "b.h"
int myputchar(char c) { return putchar(c); }

b.h:
#include <stdio.h>

c.c:
#include <curses.h>
int myputchar(char c) { return putchar(c); }

For which files do you want a notification? I'm guessing you want none for
a, and one for c. I don't know about b. However, what if <stdio.h> doesn't
define putchar, but instead includes a non-standard header which does? On
such a system (mine happens to declare putchar in <stdio.h> and define it
in <bits/stdio.h>, but the problem does exist for several other standard
library headers) there's really no difference between a and c that any
tool could tell without special built-in knowledge. So what do you want
the tool to do?
 
C

CBFalconer

Tomás Ó hÉilidhe said:
.... snip ...

Is there any utility out there that will process a source file and
notify you if you're depending on a declaration that isn't present
in a header file that's included directly in the source file?

I don't see the problem. When the compiler goes through its
linking phase it will (normally) announce the identity of any
unfound routines or objects. These indicate the absence of some
library or object module. If the missing item is in the standard
library it should have been found already, unless it is in the math
library. Otherwise the thing missing is your own code. Write it.
 
R

Richard Tobin

Tomás Ó hÉilidhe said:
Recently, there was a Linux program distributed as source code, and it
compiled fine on the majority of systems. However on some systems, it
failed to compile. On some of these systems, people were getting
errors for undeclared tokens, while others were getting linking
errors.

Anyway, the problem was that one of the source files was missing:

#include <stdio.h>

This wasn't a problem on most systems because some of the other header
files that were included actually included stdio.h.

Presumably you mean that one of the system headers included stdio.h,
because if it was one of the program's headers this wouldn't be a
problem on "some systems".

I'm not sure to what extent this is permitted for standard C headers.
Presumably it's allowed, because all the identifiers they declare are
reserved. Perhaps someone can quote chapter and verse on this.
Is there any utility out there that will process a source file and
notify you if you're depending on a declaration that isn't present in
a header file that's included directly in the source file?

I don't think that's possible, since it's perfectly legal for, say,
stdio.h to work by including some other system headers that declares
printf().

-- Richard
 
H

Harald van Dijk

Presumably you mean that one of the system headers included stdio.h,
because if it was one of the program's headers this wouldn't be a
problem on "some systems".

I'm not sure to what extent this is permitted for standard C headers.

Not at all, except in specific cases under the as-if rule.
Presumably it's allowed, because all the identifiers they declare are
reserved. Perhaps someone can quote chapter and verse on this.

Most aren't reserved (except as external identifiers) unless the header is
included; part of 7.1.3p1 is:
"Each identifier with file scope listed in any of the following subclauses
(including the future library directions) is reserved for use as a macro
name and as an identifier with file scope in the same name space if any
of its associated headers is included."

This is a strictly conforming program:

#include <stdlib.h>
static int puts = 3;
int main(void) {
return puts - 3;
}

and it wouldn't work if said:
I don't think that's possible, since it's perfectly legal for, say,
stdio.h to work by including some other system headers that declares
printf().

Right.
 
T

Tomás Ó hÉilidhe

Presumably you mean that one of the system headers included stdio.h,
because if it was one of the program's headers this wouldn't be a
problem on "some systems".


Not necessarily. The code could have used some other shared headers,
something like:

#include <openssl/decrypt.h>

The newer version of "decrypt.h" might not include <stdio.h>.
 
K

Keith Thompson

Tomás Ó hÉilidhe said:
Recently, there was a Linux program distributed as source code, and it
compiled fine on the majority of systems. However on some systems, it
failed to compile. On some of these systems, people were getting
errors for undeclared tokens, while others were getting linking
errors.

Anyway, the problem was that one of the source files was missing:

#include <stdio.h>

This wasn't a problem on most systems because some of the other header
files that were included actually included stdio.h.

Is there any utility out there that will process a source file and
notify you if you're depending on a declaration that isn't present in
a header file that's included directly in the source file?

As others have said, that's a tough problem.

Assume the following:

<sys/foo.h> has "#include <stdio.h>"
<stdio.h> has "#include <sys/puts.h>"
<sys/puts.h> has the declaration of puts; <stdio.h> does not

Your program that calls puts should definitely have
"#include <stdio.h>", not "#include <sys/puts.h>"; your proposed tool
would flag that as an error.

Your program that happens to use <sys/foo.h> and that calls puts can
get away with omitting the "#include <stdio.h>" on this particular
system, but it's not portable.

What you want to find is not which header directly declares a given
function, but which header *should* be #included by a program that
calls that function (and likewise for things other than function
declarations). This information is not available in any obvious way
by examining the system header files themselves.

But if some kind of database were built that said:

For puts, use <stdio.h>
For foo, use <sys/foo.h>
...

then a tool could use that information to diagnose the kind of problem
you're talking about. Such a tool could also diagnose unnecessary
#include directives (for example, a program that uses malloc doesn't
need "#include <malloc.h>" even if the implementation happens to
provide it as a non-standard header).

I've never heard of such a tool, but it could be useful.
 
C

CBFalconer

Harald said:
Richard Tobin wrote:
.... snip ...


Not at all, except in specific cases under the as-if rule.


Most aren't reserved (except as external identifiers) unless
the header is included; part of 7.1.3p1 is:

"Each identifier with file scope listed in any of the following
subclauses (including the future library directions) is
reserved for use as a macro name and as an identifier with file
scope in the same name space if any of its associated headers
is included."

This is a strictly conforming program:

#include <stdlib.h>
static int puts = 3;
int main(void) {
return puts - 3;
}

and it wouldn't work if <stdlib.h> includes <stdio.h>.

The actual verbiage (from N1256, since it has an extra paragraph
over N859) is:

7.1.3 Reservedidentifiers
1 Each header declares or defines all identifiers listed in its
associated subclause, and optionally declares or defines
identifiers listed in its associated future library directions
subclause and identifiers which are always reserved either for
anyuse or for use as file scope identifiers.

— All identifiers that begin with an underscore and either an
uppercase letter or another underscore are always reserved
for anyuse.
— All identifiers that begin with an underscore are always
reserved for use as identifiers with file scope in both the
ordinary and tag name spaces.
— Each macro name in any of the following subclauses
(including the future library directions) is reserved for
use as specified if any of its associated headers is
included; unless explicitly stated otherwise (see 7.1.4).
— All identifiers with external linkage in any of the
following subclauses (including the future library
directions) are always reserved for use as identifiers with
external linkage.
— Each identifier with file scope listed in any of the
following subclauses (including the future library
directions) is reserved for use as a macro name and as an
identifier with file scope in the same name space if any of
its associated headers is included.

And, as I read this, any identifiers declared in any standard
library section are reserved everywhere. This does not mean that
your program will not work, just that it MAY not work, and is NOT
strictly conforming.
 
J

Jack Klein

I don't see the problem. When the compiler goes through its
linking phase it will (normally) announce the identity of any
unfound routines or objects. These indicate the absence of some
library or object module. If the missing item is in the standard
library it should have been found already, unless it is in the math
library. Otherwise the thing missing is your own code. Write it.

You seem to have misread the question, Chuck. He is not asking about
linking and libraries, but about compiling and headers.

Read it again.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
 
C

CBFalconer

Jack said:
You seem to have misread the question, Chuck. He is not asking about
linking and libraries, but about compiling and headers.

Read it again.

Well, I reread the quoted part, and it seems to me that any
compiler should spit out some 'undefined' error messages. You
can't say a missing definition belongs in any particular file. The
fact that functions require prototypes in C99 should help. Maybe I
am thick.
 
K

Keith Thompson

CBFalconer said:
Well, I reread the quoted part, and it seems to me that any
compiler should spit out some 'undefined' error messages. You
can't say a missing definition belongs in any particular file. The
fact that functions require prototypes in C99 should help. Maybe I
am thick.

The key word is *directly*. For example, if a program calls printf,
it should have "#include <stdio.h>". If another header the program
includes, say "foo.h", happens to include <stdio.h>, then the program
will compile without error -- until it's recompiled on a system where
"foo.h" *doesn't* happen to include <stdio.h>. What Tomás is looking
for is a utility that will warn about this kind of problem.
 
P

Paul Hsieh

Recently, there was a Linux program distributed as source code, and it
compiled fine on the majority of systems. However on some systems, it
failed to compile. On some of these systems, people were getting
errors for undeclared tokens, while others were getting linking
errors.

Anyway, the problem was that one of the source files was missing:

#include <stdio.h>

This wasn't a problem on most systems because some of the other header
files that were included actually included stdio.h.

Indeed, a good question. One not addressed by the C standard. One
can see how the claims of portability seem highly exaggerated in light
of this.
Is there any utility out there that will process a source file and
notify you if you're depending on a declaration that isn't present in
a header file that's included directly in the source file?

Well ... I am unaware of such a utility.

But you could build one manually, probably. The main purpose of the
standard library header files are to declare prototypes and some
typedefs. So what you could do is copy your compiler's header files
to a separate directory structure somewhere, manually remove all
#include's from those header files (easier said than done) then point
your compiler's STD LIB include directory to that directory and try a
test compile. You should not expect this to properly link -- in fact
you should get errors, but at least it should compile.

The hard part, I suppose, is building a set of include files that
truly represented the absolute minimum support that the C language.
Some of the structures defined may have fields that depend on the
inclusion of other header files, for example. But I suppose this is a
doable exercise over a few days, maybe, if one started with a
compiler's header files, and the C standard, and just worked through
them.

You didn't think writing portable C code was going to be easy did you?
 
H

Harald van Dijk

The actual verbiage (from N1256, since it has an extra paragraph over
N859) is:

7.1.3 Reservedidentifiers
1 Each header declares or defines all identifiers listed in its
associated subclause, and optionally declares or defines
identifiers listed in its associated future library directions
subclause and identifiers which are always reserved either for
anyuse or for use as file scope identifiers.

— All identifiers that begin with an underscore and either an
uppercase letter or another underscore are always reserved for
anyuse.
— All identifiers that begin with an underscore are always
reserved for use as identifiers with file scope in both the
ordinary and tag name spaces.
— Each macro name in any of the following subclauses
(including the future library directions) is reserved for use as
specified if any of its associated headers is included; unless
explicitly stated otherwise (see 7.1.4).
— All identifiers with external linkage in any of the
following subclauses (including the future library directions)
are always reserved for use as identifiers with external linkage.
— Each identifier with file scope listed in any of the
following subclauses (including the future library directions) is
reserved for use as a macro name and as an identifier with file
scope in the same name space if any of its associated headers is
included.

And, as I read this, any identifiers declared in any standard library
section are reserved everywhere.

They're reserved "for use as identifiers with external linkage" when their
standard headers are not included, and they are reserved "for use as a
macro name and as an identifier with file scope in the same name space" if
they are. In my example, no header declaring puts is included, so puts is
not reserved for use as an identifier with file scope, and I haven't given
puts external linkage.
 
C

CBFalconer

Keith said:
.... snip ...


The key word is *directly*. For example, if a program calls
printf, it should have "#include <stdio.h>". If another header
the program includes, say "foo.h", happens to include <stdio.h>,
then the program will compile without error -- until it's
recompiled on a system where "foo.h" *doesn't* happen to include
<stdio.h>. What Tomás is looking for is a utility that will
warn about this kind of problem.

Oh, I see. However, that sort of problem is basic to his include
philosophy. When he writes the module that includes foo.h, and
needs access to printf, he should have included stdio.h himself.
stdio.h (and all standard include files) has protections against
any problems due to multiple inclusion.

However, I suspect that a decent cross-reference utility will do
the job for him.
 
R

Richard Tobin

Harald van Dijk said:
They're reserved "for use as identifiers with external linkage" when their
standard headers are not included, and they are reserved "for use as a
macro name and as an identifier with file scope in the same name space" if
they are.

Incidentally, I find this wording very unclear. "Reserved for use as X"
naturally means "can only be used as X", but is presumably intended to
mean "reserved in such a way that the user mustn't re-use them as X".

-- Richard
 
H

Harald van Dijk

Incidentally, I find this wording very unclear. "Reserved for use as X"
naturally means "can only be used as X", but is presumably intended to
mean "reserved in such a way that the user mustn't re-use them as X".

It can be read from the implementation's viewpoint: the implementation can
only use puts in the ways directly specified in the standard (and mustn't
interfere with any other ways it might be used in user code).
 
C

Chris Torek

[Snippage - but the setup amounts to:

% cat nonstandard.c
/* forgotten: #include <stdio.h> */
#include <nonstandardio.h>
void func(void) { use(puts); }

where <nonstandardio.h> on System A has a #include <stdio.h> in it,
but <nonstandardio.h> on System B does not. Then nonstandard.c
compiles file on System A, but not on System B.]

Indeed, a good question. One not addressed by the C standard.

Deliberately so.
One can see how the claims of portability seem highly exaggerated
in light of this.

This is a bit like saying that the claims of extensive understandability
of the English language (i.e., English acting as a "lingua franca",
which is kind of a pun in itself) seem highly exagerrated when one
is on an alien planet. :)

To a large extent, though, this is what standards (plural) are all
about. If you write exclusively in Standard C, you will never use
"#include <nonstandardio.h>", and you will never encounter the
problem. Of course, if you *need* the facilities of <nonstandardio.h>
to achieve the goals of the program you are writing, you must
abandon (or at least augment) Standard C. You are now in territory
where the C standard not only *does* not, but *cannot* help you.
If there is another standard, you can use that; if not, you are on
your own, as it were.

(If we were to put all features from POSIX, Microsoft Windows
variants, VMS, Tandem NonStop, IBM AS/400, and every other system
into the C Standard, we would have something so unweildy -- and
self-conflicting, in some cases -- as to be useless.)
You didn't think writing portable C code was going to be easy did you?

Indeed. Sometimes -- as, for instance, when one wants to do graphics
-- it is literally impossible to do it with ISO C. Stepping outside
of the boundaries of Standard C (and comp.lang.c) generally decreases
the portability of the code, but is often necessary and/or desirable.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top