-
I disagree - if it's possible for something to be automatically checked, then ideally it should be done. Sure, it's possible in theory to never make a mistake in including the right headers, just like it's possible to write object-oriented code in C. A particular compiler may need certain header dependencies, but if the standard doesn't require them, there should (ideally) be checking to make sure they aren't hiding omission of generally required headers.
I think a simple way to check is to search the source file for strings associated with particular header files and then check whether the header is actually included - for example, if it contains "numeric_limits", then <limits> should be included. I'm not sure if this scheme could be made completely reliable, but programs like lint already flag a lot of perfectly good code so it shouldn't be any worse than that. This is basically what a programmer does - there's a mental map between tokens and header files. Less experienced ones just haven't committed the whole map to memory yet.
Edit: I think that if there exists an algorithm to determine for certain whether a particular type of mistake has been made, then ideally the compiler should implement it. Programs like lint should only be used to check for things that _may_ be mistakes.
-
I disagree with your last statement, robatino. The compiler should only perform checks that can be performed with reasonable performance. Checking that every required standard header comes with quite an impact: the compiler must have a list of standard headers and symbols from there, and it must check for every symbol whether it could come from the standard library and if it does, whether the appropriate header has been included. It must also make the distinction between usages that require full definitions and those where declaration suffices, mostly because of the <iosfwd> header. Seems like a lot of additional effort to me.
As such, this test should be automated, but performed by an external tool or enabled by a compiler switch.
-
You're right - if there is a large performance hit, it's better to have the test done separately. The tool wouldn't have to be used except just before releasing code, since during the debugging process on one's own machine the "missing" headers wouldn't make any difference.
-
I would like to add the problem of distinction between headers. It's obviously quiet common for header files to include other header files (we wouldn't be discussing it otherwise). The "nested" header file may declare the specific object we are using at a certain point, however, it's inclusion may not make much sense since the "main" header inclusion is already there, and possibly more meaningful.
How would a compiler know the difference? It's best for a third party tool like lint software to do this job.
-
How would a 3rd party tool know the difference?
Obviously this only works for system headers that have a headercontents.conf (or whatever) supplied, no matter which tool does it.
-
The third party tool wouldn't know the difference either. However, it wouldn't bog your compiler with erroneous warning messages. Something that a compiler shouldn't do.
-
Erroneous warning messages? What's erroneous about "Your program does not comply with the C++ standard and might not compile on other compilers."?
-
I don't think I expressed myself correctly.
Point 1.
The issue of header dependencies is even more prevalent in non standard headers. Boost, for instance, is known to provide "frontend" headers for a number of its libraries. And we as users also use similar techniques, or any others we can think of. The compiler could only really do this check for standard headers, limiting its usefulness, and forcing the parser to match names against a table of allowed names, limiting its speed.
Point 2.
When will this check be made? Correct me if I'm wrong, but not at the pre-processing stage where names are not checked. During compilation the headers are already included, so no warning could be issued since the names are already in scope. Unless the pre-processor left some markers for the compiler to interpret...
Point 3.
One little thing that can be solved by "know your libraries" (one of the commandments of good code), will increase considerably compile times. Even if seen as a switch we can use or not, this type of test couldn't really be used on a big project where it would make the most sense, without an impact on productivity.
Point 4.
How many warning messages are too many? In other words what will flag the warning? I suppose a reference being made on the code to a name that is only being defined on a "inner" header. What if I make 30 references to that name on a single unit? What about other units? Do we limit to 1 warning message per unit? Does it make sense? What about 15 different names on a single unit? And what about other units? This "little", "helpful", "innocent", and "useful" check would have the potential to quickly fill my maximum error messages limit and cancel compilation with more important warnings or errors being left out.
Quiet frankly, the solution is to know the standard library. Or to use external code parser like lint tools.
All in all, using a name declared on an "inner" header file is not an error, is not even worth a warning. It is a standard practice. Be it a standard library name or not. Maybe it's shocking if it's a std::string being used through <iostream>, I agree. But certainly it's much less shocking if it's NULL being used through <stdlib>.
Know your library.
-
is it a standard that the header should be included?
-
Yes, of course. A name that hasn't been defined is a compile-time error.
Where that header is included is a different matter altogether. If directly through an include directive, or indirectly through header dependencies is left for the programmer to decide. Common sense and good programming practices should dictate that, particularly on standard headers, the header where a given name is declared should be directly included. It eases portability.
-
In particular, one should never rely on any headers that are not part of the current project to include anything else, unless they're explicitly documented as doing so.