Fixing header file dependencies
DEPENDS
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 it1. Create a file called check-header
and paste in the following:
#!/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 directory2:
$ 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 theFile
class, your header file can just forward declareclass File;
instead of having to#include "file/base/file.h"
.
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.