Fixing header file dependencies

2008-07-02, , Comments

DEPENDS

Dependencies

Without care C++ header files can deteriorate, so I was interested to find some sensible advice in the Google C++ Style Guide.

Names and Orders of Includes

Use standard order for readability and to avoid hidden dependencies: C library, C++ library, other libraries’ .h, your project’s .h.

The preferred ordering reduces hidden dependencies. We want every header file to be compilable on its own. The easiest way to achieve this is to make sure that every one of them is the first .h file #included in some .cc.

I agree, hidden dependencies are bad, and I’m not about to quibble with the “standard order” defined by the guide, even if I’m used to a slightly different ordering. Certainly headers should be compilable on their own; but I suggest the easiest way to achieve this is, well, to compile them on their own.

README

You don’t need a project file or even a makefile if you want to compile something. It’s easy to create a script which confirms a header has no hidden dependencies by including and compiling it[1]. Create a file called check-header and paste in the following:

check-header
#!/bin/bash
cat <<EOF >tmp.cc && g++ $CPPFLAGS tmp.cc
#include "$1"
int main() { return 0; }
EOF

Make sure check-header is executable. Put it somewhere on your PATH. Export suitable CPPFLAGS for your codebase (here, I’m choosing to treat warnings as errors).

$ chmod a+x check-header
$ mv check-header ~/bin
$ export CPPFLAGS="-Wall -Werror"

Given a header file, check-header redirects a here document into a temporary source file. The source file contains a minimal C++ program which does nothing more than include the header. Check-header compiles that program. Compilation diagnostics, if any, appear on standard error. The exit status will be 0 if the header compiles cleanly, non-zero otherwise.

$ check-header standalone.h
$ echo $?
0
$ cat > depends_on_x.h
void f(X x);
$ check-header depends_on_x.h
depends_on_x.h:1: error: variable or field 'f' declared void
depends_on_x.h:1: error: 'X' was not declared in this scope
$ echo $?
1

INSTALL

It gets tiresome to run this command on one header at a time. Happily we can use it in a compound command to check all the headers in the current directory[2]:

$ for header in *.h; do check-header $header; done

Or on all headers beneath the current directory:

$ find . -name "*.h" | xargs -L 1 check-header

(The -L 1 is required because check-header can only handle one file at a time.)

Make this part of your overnight build, and you’ve got an easy way to monitor dependencies in header files.

BUGS

The script shown here is about the simplest thing which could possibly work. Just a single compiler is used, the temporary file name is hard-wired, no clean-up is done, there’s a dependency on the shell environment, diagnostics are limited, there isn’t even any command-line help. A grubby header can sneak past this script by using preprocessor defines for conditional compilation, and different (versions of) compilers will disagree on what’s clean.

The truth is that I usually recreate this script and variants of it as and when required. My real intention is to demonstrate the rather obvious idea that we should use the compiler to detect compilation problems.

TODO

Once your headers include all they depend on, maybe you’d like to tackle the flip side of the problem, of determining which includes they don’t or shouldn’t depend on.

Can another tip from the Google Style Guide be automated?

Use forward declarations to minimize use of #include in .h files.

You can significantly minimize the number of header files you need to include in your own header files by using forward declarations. For example, if your header file uses the File class in ways that do not require access to the declaration of the File class, your header file can just forward declare class File; instead of having to #include "file/base/file.h".

Book cover

I don’t think many of us would dispute this advice, though I’m not sure it belongs in a style guide — it’s just good C++ practice, the stuff you should be getting from a book. What I’d like is a refactoring tool which does it for me, something like Eclipse’s “organize imports”. A script might be able to do some of this, but it will have to be considerably more complex than check-header, and without access to the compiler internals it will be limited in power.


[1] While writing this article I discovered GCC allows you to precompile header files, reducing the need to create even a minimal script like check-header. Running gcc $CPPFLAGS header.h generates header.gch for a valid and self-contained header file, and compiler diagnostics otherwise. As the documentation says:

There are many other possibilities, limited only by your imagination, good sense, and the constraints of your build system.

[2] It would be better to tweak check-header to work on a list of input files.