Code Better: Headers without Hidden Dependencies

When you work on a larger project, you cannot easily keep track of which header depends on which other header. You can (and should) do your best to keep the number of other headers referenced inside your headers low (to speed up compilation) and move as many header dependencies as you can into your source files, but this still doesn’t prevent you from building headers that implicitly depend on another header being included before them.

Take this example:

#ifndef MAP_H
#define MAP_H

/// <summary>Stores the tiles of a 2D map<summary>
struct Map {};

#endif // MAP_H
#ifndef WORLD_H
#define WORLD_H

#include "Actor.h"
#include <vector>
// Oops, forgot Map.h, but won't notice since World.cpp includes Map.h before World.h

/// <summary>Maintains the state of the entire world<summary>
struct World {
  /// <summary>Stores the map as a grid of 2D tiles</summary>
  public: Map Map;
  /// <summary>Actors (player, monsters, etc.) currently active in the world</summary>
  public: std::vector<Actor *> Actors;
};

#endif // WORLD_H

Throughout your project, map.h might always end up being included before world.h and you might never notice that if someone included world.h on its own, a nasty compilation error would be the result.

So what can you do ensure this situation never happens?

Solution

It’s actually pretty simple: each header should have an associated source file. And the first thing that source file should do is to include its associated header. Always.

  • If a header just declares a silly enumeration — add a source file that does nothing else but include the header.
  • If a header just provides a convenient selection of other headers — add a new source file that does nothing else but include that header.
  • And if a header declares a class with many methods whose method bodies are in a source file that source file — you guessed it — should include its own header first thing.

This ensures that any header in your entire project is at least once included before any other headers, thus, if any header on its own produced a compilation error, you would catch it while compiling your project.

I’ve designed entire frameworks and games following this rule and it eliminated all hidden dependencies from my header, resulting in nothing but good, usable design!