Self-sufficient header files in C/C++
I recently posted a question asking for what actions would constitute the Zen of C++. I received excellent answers, but I could not understand one recommendation:
- Make header files self-sufficient
How do you ensure your header files are self-sufficient?
Any other advice or best-practice related to the design and implementation of header files in C/C++ will be welcome.开发者_如何学C
Edit: I found this question which addresses the "Best Practices" part of mine.
A self sufficient header file is one that doesn't depend on the context of where it is included to work correctly. If you make sure you #include or define/declare everything before you use it, you have yourself a self sufficient header.
An example of a non self sufficient header might be something like this:
----- MyClass.h -----
class MyClass
{
MyClass(std::string s);
};
-
---- MyClass.cpp -----
#include <string>
#include "MyClass.h"
MyClass::MyClass(std::string s)
{}
In this example, MyClass.h uses std::string
without first #including .
For this to work, in MyClass.cpp you need to put the #include <string>
before #include "MyClass.h"
.
If MyClass's user fails to do this he will get an error that std::string is not included.
Maintaining your headers to be self sufficient can be often neglected. For instance, you have a huge MyClass header and you add to it another small method which uses std::string. In all of the places this class is currently used, is already #included before MyClass.h. then someday you #include MyClass.h as the first header and suddenly you have all these new error in a file you didn't even touch (MyClass.h)
Carefully maintaining your headers to be self sufficient help to avoid this problem.
NASA's Goddard Space Flight Center (GSFC) has published C and C++ programming standards that address this issue.
Assume you have a module with a source file perverse.c
and its header perverse.h
.
Ensuring a header is self-contained
There is a very simple way to ensure that a header is self-contained. In the source file, the first header you include is the module's header. If it compiles like this, the header is self-contained (self-sufficient). If it does not, fix the header until it is (reliably1) self-contained.
perverse.h
#ifndef PERVERSE_H_INCLUDED
#define PERVERSE_H_INCLUDED
#include <stddef.h>
extern size_t perverse(const unsigned char *bytes, size_t nbytes);
#endif /* PERVERSE_H_INCLUDED */
Almost all headers should be protected against multiple inclusion. (The standard <assert.h>
header is an explicit exception to the rule — hence the 'almost' qualifier.)
perverse.c
#include "perverse.h"
#include <stdio.h> // defines size_t too
size_t perverse(const unsigned char *bytes, size_t nbytes)
{
...etc...
}
Note that even though it was traditionally considered a good idea to include the standard headers before the project headers, in this case, it is crucial to the testability that the module header (perverse.h
) comes before all others. The only exception I'd allow is including a configuration header ahead of the module header; however, even that is dubious. If the module header needs to use (or maybe just 'can use') the information from the configuration header, it should probably include the configuration header itself, rather than rely on the source files using it to do so. However, if you need to configure which version of POSIX to request support for, that must be done before the first system header is included.
Footnote 1: Steve Jessop's comment to Shoosh's answer is why I put the parenthesized '(reliably)' comment into my 'fix it' comment. He said:
Another factor making this difficult is the "system headers can include other headers" rule in C++. If
<iostream>
includes<string>
, then it's quite difficult to discover that you've forgotten to include<string>
in some header which does [not] use<iostream>
[or<string>
]. Compiling the header on its own gives no errors: it's self-sufficient on this version of your compiler, but on another compiler it might not work.
See also the answer by Toby Speight about IWYU — Include What You Use.
Appendix: Matching these rules with GCC Precompiled Headers
The GCC rules for precompiled headers permit just one such header per translation unit, and it must appear before any C tokens.
GCC 4.4.1 Manual, §3.20 Using Precompiled Headers
A precompiled header file can be used only when these conditions apply:
- Only one precompiled header can be used in a particular compilation.
- A precompiled header can’t be used once the first C token is seen. You can have preprocessor directives before a precompiled header; you can even include a precompiled header from inside another header, so long as there are no C tokens before the #include.
- [...]
- Any macros defined before the precompiled header is included must either be defined in the same way as when the precompiled header was generated, or must not affect the precompiled header, which usually means that they don’t appear in the precompiled header at all.
To a first approximation, these constraints mean that the precompiled header must be the first in the file. A second approximation notes that if 'config.h' only contains #define statements, it could appear ahead of the precompiled header, but it is much more likely that (a) the defines from config.h affect the rest of the code, and (b) the precompiled header needs to include config.h anyway.
The projects I work on are not set up to use pre-compiled headers, and the constraints defined by GCC plus the anarchy induced by over 20 years of intensive maintenance and extension by a diverse population of coders mean it would be very hard to add them.
Given the divergent requirements between the GSFC guidelines and GCC precompiled headers (and assuming that precompiled headers are in use), I think that I would ensure the self-containment and idempotence of headers using a separate mechanism. I already do this for the main projects I work on — reorganizing the headers to meet the GSFC guidelines is not an easy option — and the script I use is chkhdr
, shown below. You could even do this as a 'build' step in the header directory — ensure that all the headers are self-contained as a 'compilation' rule.
chkhdr script
I use this chkhdr
script to check that headers are self-contained. Although the shebang says 'Korn shell', the code is actually OK with Bash or even the original (System V-ish) Bourne Shell.
#!/bin/ksh
#
# @(#)$Id: chkhdr.sh,v 1.2 2010/04/24 16:52:59 jleffler Exp $
#
# Check whether a header can be compiled standalone
tmp=chkhdr-$$
trap 'rm -f $tmp.?; exit 1' 0 1 2 3 13 15
cat >$tmp.c <<EOF
#include HEADER /* Check self-containment */
#include HEADER /* Check idempotency */
int main(void){return 0;}
EOF
options=
for file in "$@"
do
case "$file" in
(-*) options="$options $file";;
(*) echo "$file:"
gcc $options -DHEADER="\"$file\"" -c $tmp.c
;;
esac
done
rm -f $tmp.?
trap 0
It so happens that I've never needed to pass any options containing spaces to the script so the code is not sound in its handling of options of spaces. Handling them in Bourne/Korn shell at least makes the script more complex for no benefit; using Bash and an array might be better.
Usage:
chkhdr -Wstrict-prototypes -DULTRA_TURBO -I$PROJECT/include header1.h header2.h
GSFC Standard available via Internet Archive
The URL linked above is no longer functional (404). You can find the C++ standard (582-2003-004) at EverySpec.com (on page 2); the C standard (582-2000-005) seems to be missing in action.
However, the referenced NASA C coding standard can be accessed and downloaded via the Internet archive:
http://web.archive.org/web/20090412090730/http://software.gsfc.nasa.gov/assetsbytype.cfm?TypeAsset=Standard
See also:
- Should I use
#include
in headers? - How to link multiple implementation files in C?
- Professional
#include
contents? - Where to document functions in C or C++?
Make sure you include everything you need in the header, instead of assuming that something you included includes something else you need.
Old question, new answer. :-)
There is now a tool called include-what-you-use which is designed to analyse your code for exactly this kind of problem. On Debian and derived systems, it can be installed as the iwyu
package.
The idea is that a header file does not depend on a previous header file in order to compile. Therefore the order of the header files is not significant. Part of doing this is including in a header file all the other header files it will need. The other part is ifdef'ing your headers so that they aren't processed more than once.
The idea is that if you need to add a foo object to your class you just need to #include foo.h and you don't need to bar.h in front of it in order to get foo.h to compile (e.g. there is a call in foo that returns a bar object instance. You may not be interested in this call but you will need to add bar.h to let the compiler know what is being referenced).
I'm not sure I would always agree with this advice. A large project will have hundreds of header files and the compile will end up reading through the common ones of them hundreds of times just to ignore the #ifdefs. What I have seen done in this case is a header file of header files that is standard for the project and includes the thirty common ones. It is always first in the list of includes. This can speed up compile time but makes the maintenance of the general header a skilled task.
You'd want to use the method described in the GNU C Preprocessor Manual:
2.4 Once-Only Headers
If a header file happens to be included twice, the compiler will process its contents twice. This is very likely to cause an error, e.g. when the compiler sees the same structure definition twice. Even if it does not, it will certainly waste time.
The standard way to prevent this is to enclose the entire real contents of the file in a conditional, like this:
/* File foo. */ #ifndef FILE_FOO_SEEN #define FILE_FOO_SEEN
the entire file
#endif /* !FILE_FOO_SEEN */
This construct is commonly known as a wrapper
#ifndef
. When the header is included again, the conditional will be false, becauseFILE_FOO_SEEN
is defined. The preprocessor will skip over the entire contents of the file, and the compiler will not see it twice.CPP optimizes even further. It remembers when a header file has a wrapper ‘
#ifndef
’. If a subsequent ‘#include
’ specifies that header, and the macro in the ‘#ifndef
’ is still defined, it does not bother to rescan the file at all.You can put comments outside the wrapper. They will not interfere with this optimization.
The macro
FILE_FOO_SEEN
is called the controlling macro or guard macro. In a user header file, the macro name should not begin with ‘_
’. In a system header file, it should begin with ‘__
’ to avoid conflicts with user programs. In any kind of header file, the macro name should contain the name of the file and some additional text, to avoid conflicts with other header files.
This is a great question. I think I will re-examine the practice of putting a stdafx.h
as the first include in each .cpp file when using Visual Studio. If you use pre-compiled header files, it doesn't matter anyway, might as well have friendlier header files.
Thanks jalf for correction. From Wikipedia
Visual C++ will not compile anything before the #include "stdafx.h" in the source file, unless the compile option /Yu'stdafx.h' is unchecked (by default); it assumes all code in the source up to and including that line is already compiled.
So this means that pre-compiled headers break the self-sufficient header rule, right?
Having not seen your other question, my first thought around this would be protecting my header files from multiple calls (let my headers fend for themselves).
#ifndef MY_PROTECTED_HEADER_H
#define MY_PROTECTED_HEADER_H
/*
* Stuff here
*/
#endif /* MY_PROTECTED_HEADER_H */
精彩评论