ACCU Home page ACCU Conference Page ACCU 2017 Conference Registration Page
Search Contact us ACCU at Flickr ACCU at GitHib ACCU at Google+ ACCU at Facebook ACCU at Linked-in ACCU at Twitter Skip Navigation

pinQM Bites – Order Your Includes (Twice Over)

Overload Journal #133 - June 2016 + Programming Topics   Author: Matthew Wilson
Header includes can be a shambles. Matthew Wilson encourages us to bring some order to the chaos.

TL;DR

Order includes in groups of descending specificity and lexicographically within groups

Bite

Consider the following example of #includes in source file Cutter.cpp, containing the implementation of a class Cutter for a fictional organisation AcmeSoftware with a product Blade. In this case, the class’s implementation and header files are located in the same source directory; this need not always be so, but the discussion to follow still applies.

  #include "stdafx.h"
  #include <vector>
  #include <string>
  #include <acmecmn/string_util2.h>
  #include <acmecmn/string_util1.h>
  #include <Blade/Sharpener.hpp>
  #include <Blade/Protector.hpp>
  #include "Cutter.hpp"
  #include <stdlib.h>
  #include <map>

This is quite wrong. Here’s the right way to do it:

  #include "stdafx.h"
  
  #include "Cutter.hpp"
  
  #include <Blade/Protector.hpp>
  #include <Blade/Sharpener.hpp>
  
  #include <acmecmn/string_util1.h>
  #include <acmecmn/string_util2.h>
  
  #include <map>
  #include <string>
  #include <vector>
  
  #include <stdlib.h>

This has been ordered according to descending order of specificity of groups, and then lexicographically within groups. The drivers are, respectively, modularity and transparency.

The reason for descending order of specificity of groups is to expose hidden dependencies – coupling! – in any of the header files. Unlike languages such as Java and C#, the order of ‘imports’, in the form of #includes, has significance in C and C++. For example, if Cutter.hpp makes use of std::vector but does not itself include that file then compilation units that include it are at risk of compile error; the original order masks that. The same rationale applies to the files in other groups: if Blade/Sharpener.hpp requires a definition in acmecmn/string_util1.h then this would also be exposed.

The reason for lexicographical ordering with groups is to make it easier to comprehend each group’s contents. In the real world there can be many tens of included files, and if not in some readily comprehensible order then duplicates can more easily occur, which is then obviously a problem when trying to remove unnecessary includes. (Admittedly, by having a strict lexicographical ordering within groups there is a slightly increased possibility of hiding interheader coupling between files in a group, but unless you want to go to some extreme such as reverse lexicographical ordering for headers and forward for implementation files – which I do not advise – you’ll have to wear this slight risk. The grouping will take care of the vast majority.)

The reason for a blank line between groups is obvious: to delineate one group from another to further aid transparency.

Usually, the most specific group – the Level-1 group in an implementation file – would be its declaring header(s), containing declarations of its API functions and/or defining its class: in this case Cutter.hpp. Note that it makes no difference whether the declaring header is in the same directory, i.e. #include "Cutter.hpp", or in another directory, e.g. #include <AcmeSoft/Blade/Cutter.hpp>: its pre-eminence is unchanged.

Sometimes, for implementation reasons, we have to have a Level-0 group – in this case this is the precompiled header include file stdafx.h. (In a soontobecooked Bite I will discuss why and how you should get rid of the presence of these things from your source.)

Similarly, for very rare implementation reasons, we have to have a Level-N group. I have not shown such in this case, but if you’ve chomped on enough C++ in your time you’ll have experienced such things, perhaps to conduct some shameful but necessary preprocessor surgery after all includes but before any implementation code is translated.

Hence, the rule is to order includes:

  1. Order all files in groups of descending order of specificity, with each group separated by a blank line, including:
    1. Explicit Level-0 includes (if required);
    2. Level-1 include(s): the declaring header(s) file (for implementation files only);
    3. Other include groups for the given application component;
    4. Other include groups for the organisation;
    5. Other include groups for 3rdpartysoftware;
    6. Standard C++ headers;
    7. Standard C headers;
    8. Explicit Level-N includes (if required).
  2. Lexicographically order all includes within groups;

Further Reading

C++ Coding Standards, Herb Sutter & Andrei Alexandrescu, AddisonWesley, 2004

Large Scale C++ Software Design, John Lakos, AddisonWesley, 1996

Overload Journal #133 - June 2016 + Programming Topics