views:

588

answers:

7

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.

Edit: I found this question which addresses the "Best Practices" part of mine.

+16  A: 

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 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.

shoosh
@shoosh: is it bad when different header files call the same, third, header file? For instance, if two .h files call math.h.
Arrieta
@Arrieta: See jeremyosborne's answer. System header files generally guard against being included twice in this way, and so should yours.
Artelius
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 use <iostream>. 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.
Steve Jessop
@Steve: I gave you a +1 comment even though I think you meant "in some header which does _not_ use `<iostream>`. This issue is why I parenthesized '(reliably)' in my answer.
Jonathan Leffler
Correct that I typed the wrong thing. I actually meant to say, "does include <iostream>". Header A includes <iostream> but does not include <string>. Header A uses std::string. On implementation B (where <iostream> includes <string>), A appears self-sufficient. On implementation C (where <iostream> does not include <string>), A is shown not to be self-sufficient.
Steve Jessop
+2  A: 

Make sure you include everything you need in the header, instead of assuming that something you included includes something else you need.

FranticPedantic
A: 

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 */

EDIT: Please note that my original response was incorrect, hence the reason you might see the negative score on this response. I have corrected my original mistake.

jeremyosborne
That doesn't look right. I think you want to swap the first two lines, and change #ifdef to #ifndef. So line 1: #ifndef, line 2: #define, line n: #endif
Bill Forster
This is wrong way.-1
NightCoder
-1: this code is completely incorrect. What you have done is create a define and then immediately test that it exists (which it does, you just defined it). It should be `#ifndef XXX` and `#define XXX`.
Evan Teran
You are all very right, and I'm embarrassed that I posted this without checking. My apologies, I'm going to edit my response now, although I understand it is quite late.
jeremyosborne
+3  A: 

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.

verisimilidude
A: 

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, because FILE_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.

Teddy
+7  A: 

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 is often 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.


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.


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
Jonathan Leffler
Ah, those Goddard evil overlords. I will check it out.
Arrieta
Now I wish there was a way to "marry" this principle with precompiled headers, which also need to come first thing. Any tips?
romkyns
@romkyns: see the appendix added.
Jonathan Leffler
+1  A: 

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 metter 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 self-sufficient header rule, right?

Igor Zevaka
With precompiled headers it *does* matter. The assumption that the precompiled header is the first one included is what makes it possible. Include another header first, and you change the compiler state, and it all comes crashing down.Which is one reason I don't much like precompiled headers. It's such a clumsy, fragile solution. Definitely not something I'd use "by default". Only when compile times force me to do it.
jalf
Interesting, I suppose I should read up on that.
Igor Zevaka
@jalf unfortunately my own experience is that the moment you include significant portions of boost, std or windows (i.e. almost any windows app larger than "tiny") you need precompiled headers :/
romkyns