QM Bites – Order Your Includes (Twice Over)

QM Bites – Order Your Includes (Twice Over)

By Matthew Wilson

Overload, 24(133):7, June 2016


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 #include s 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 #include s, 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






Your Privacy

By clicking "Accept Non-Essential Cookies" you agree ACCU can store non-essential cookies on your device and disclose information in accordance with our Privacy Policy and Cookie Policy.

Current Setting: Non-Essential Cookies REJECTED


By clicking "Include Third Party Content" you agree ACCU can forward your IP address to third-party sites (such as YouTube) to enhance the information presented on this site, and that third-party sites may store cookies on your device.

Current Setting: Third Party Content EXCLUDED



Settings can be changed at any time from the Cookie Policy page.