Header File Conventions

At some point, a person learning to program in C/C++ learns the distinction between “header' files and “source” files. That is, a source file is compiled by the compiler and header files are included by and shared among the source files. Quickly, the question becomes, “How do I use header files effectively?” This is a C/C++-centric description of the modern conventions for what goes into header files, how they are organized, and how they are used.

A short history of header files

In the Middle Ages of the Computer Era (that would be the 1980's), the “header” file was born, and it was used to store all the constants and declarations that a “source” file would need. It included internal details and references to external functions (using the evil “extern” keyword). Header files were regarded as the file equivalent of a macro and they were used primarily for convenience.

A typical header file for main.c looked like this:

/* main.h (included by main.c) */

/* Header files needed by main.c */

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <math.h>

/* External data referenced by main.c */

extern char z[128];

/* Functions in foo.c that are called by main.c */

extern int a( char *);
extern int b( char *);

/* Functions defined and used only in main.c */

static int x();
static int y();

/* Constants used by main.c */

#define X		1
#define Y		2

Those days are gone (and good riddance!). With the birth of OOP, it was realized that the way header files were being used was backwards, and that it is much better to use a header file as an “API” for a source file or module. The declarations in the header file describe what constants, types, data, and functions the module provides. That is the convention in common use today.

What goes in a header file

As stated above, a header file is used to describe the API for a module (or source file). It contains declarations of all the constants, types, data, and functions provided by the module.

Here are the things that go in a header file:

  • include guards
  • the header files needed to compile the rest of the header file - no more, no less (except in the case of meta-header files and precompiled header files)
  • forward declarations (preferred to including header files)
  • declarations of constants that are part of what the module provides
  • declarations of the classes, structs and other types implemented by the source file that are available for use by others (private types should not be declared in the header file)
  • declarations of functions implemented in the source file that are available for use by others (“static” functions are not be declared in the header file)
  • declarations of data in the source file that is available for use by others (a.k.a. global data)
  • inline functions (though it is frequently better to place these in a separate file)

What does not go in a header file

In the current convention, exposing internal constants, types, data, and functions to the world is a no-no. None of the lines in the medieval code example above should be included in a header file.

Here is what does not go in a header file for a module:

  • header files needed only by the module
  • constants, types, data, and function declarations that are private to the module
  • any other unnecessary implementation details about the module

Including header files

The order of inclusion of header files is mostly a matter of preference, with the exception of one requirement and one very useful convention. Here is the recommended order:

  • (optional) Necessar header files that are already included in the precompiled header file. If a PCH file is used, all text before the line that includes the precompiled header is ignored. It can be useful to duplicate the headers in case the file is compiled with a dummy PCH file and PCH is turned off. The drawback is that they can be difficult and/or tedious to maintain.
  • The precompiled header file (if used). The precompiled header is required to be included before all others because all text before the precompiled header is ignored.
  • This file's header file. Including the source file's header file before all others (except the precompiled header) is very useful. When you compile the source file, you will also test its header file to make sure it is “self-sufficient”. Self-sufficiency is an important quality of header files. Self-sufficient header files do not depend on the order of inclusion, and help catch and prevent circular dependencies.
  • All the rest of the header files The order of the rest of the headers is generally a matter of preference (assuming that they are all self-sufficient). However, sometimes the order might be dictated by coding standards. In the absence of standards, here is a suggestion:
    • Group by scope, and order scopes from the most local to the most global. Ordering from the most local to the most global has two purposes: it extends the test for self-sufficiency and it allows local headers to override global/system headers. Is all this important? Not really.
    • Within each scope, group by module and order modules alphabetically.
    • Within each module, order files alphabetically.

Here's an example:

// controller.c

#include "precompiledheader.h"

// This file’s header file
#include "controller.h"

// Header files in this file’s module
#include "gamemenu.h"
#include "menus.h"
#include "preferences.h"

// Header files in sibling or parent modules
#include "System/UserInput/userinput.h"
#include "System/UserInput/vibration.h"

// Header files in unrelated modules
#include "Zap/button.h"
#include "Zap/cycler.h"
#include "Zap/dialog.h"
#include "Zap/zap.h"

// System header files
#include <core/pad_t.h>

// Standard library header files
#include <stdio.h>


QR Code
QR Code header_file_conventions (generated for current page)