Skip to content

pmiddend/smartincludes

Repository files navigation

smartincludes — order your C++ includes like a boss

https://travis-ci.com/pmiddend/smartincludes.svg?branch=master

What is this?

smartincludes rewrites your C++ #include blocks. You know, the ones usually at the top of the file. In short:

  • it sorts the includes
  • removes duplicates
  • optionally forms groups of includes and orders those by a “rank” system (see below)
  • it optionally surrounds “external” includes with a special header

Philosophy

smartincludes is based upon a few assumptions and good practices. These are described here:

Project-local warning flags

Special warning flags like -Wall or -Wextra are useful, and you should use them. However, we want to only apply them to our headers, not somebody else’s, because usually, we cannot fix those “external” warnings easily.

In order to do that, we have to add special compiler commands before we include external headers, and also add special compiler commands after we are done including those external headers.

So it looks like that

#include <myproject/a.hpp>
#include <myproject/b.hpp>
#pragma magic begin
#include <boost/someheader.hpp>
#pragma magic end

heres your code

Of course, adding those extra headers is tedious and error-prone. Wouldn’t it be great to have a program that adds those headers automatically? Well…you’re looking at it. For that, you have to have a separate header file which contains these #pragma magic commands. Let’s call those myproject/external_begin.hpp and myproject/external_end.hpp For example, the following command line auto-adds those headers:

smartincludes --library myproject --external-header-pair myproject/external_begin.hpp,myproject/external_end.hpp myfile.cpp

How does it know which headers are external? Well, everything you specify using --library is automatically “not external”. The resulting file will look like this:

#include <myproject/a.hpp>
#include <myproject/b.hpp>
#include <myproject/external_begin.hpp>
#include <boost/someheader.hpp>
#include <myproject/external_end.hpp>

heres your code

Sorting headers topologically

Adding the correct headers for your C++ source file is hard, since C++ lacks a real “module” system. If you have a class foo in file foo.hpp such as this:

class foo {};

and a class bar in bar.hpp that uses foo:

#include <foo.hpp>

class bar : foo {};

Then, of course, you need to include foo.hpp. However, if you have another class baz which uses foo, you now have two possibilities for including it foo. This, however wrong it might seem, is correct:

#include <bar.hpp>

class baz : foo {};

Because the compiler/preprocessor just “copy and pasting” text when compiling the latter fragment, you can go wild with transitive includes.

The code above compiles just fine, until you rewrite something and bar not doesn’t need foo anymore. Including bar.hpp then doesn’t import foo and you get an error compiling. Bummer. This seems a bit trivial, because it is, but in bigger projects, this problem unveils in the following way:

#include <lib1/foo.hpp>
#include <yourproject/x.hpp>
#include <lib2/bar.hpp>
#include <yourproject/y.hpp>
#include <lib3/baz.hpp>

The code with this include order might “magically” compile, because, for example, x.hpp includes just the right things to make y.hpp further down compile, and then to make the whole source file compile. But as soon as you switch around your includes (swap foo.hpp and bar.hpp, for instance), it breaks.

To detect (not fix, mind you) these errors, we can at least order our includes so that the libraries that are used by other libraries are included first, as such:

#include <lib1/foo.hpp>
#include <lib2/bar.hpp>
#include <lib3/baz.hpp>
#include <yourproject/x.hpp>
#include <yourproject/y.hpp>

This way, assuming lib1, lib2, lib3 are “safe”, your project is “sort of safe” from mixups, too.

And this is exactly what the (repeatable) --library parameter is for: specifying which libraries are external to your app, and in which order the include blocks using this library should be placed.

Installation

Currently, only installation via the Nix package manager is supported. This allows for reproducible, cross-platform builds. Building (and running, see last line) is as easy as:

git clone https://github.com/pmiddend/smartincludes.git
cd smartincludes
nix-build
result/bin/smartincludes --help

Usage

The command line parameters (which are shown when you run smartincludes --help) are described in detail above. The general usage is simply:

smartincludes < $inputfile > $outputfile

It’ll read its input from stdin and write it to stdout.

About

Sort your C++ include lists like a boss!

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published