I think most (good) programmers tend to prefer tidy code, having some habits or rules to stick to. One such rule/habit is the order of #include
directives. While having a rule of thumb to stick to is good, some are better than other.
A very common one I think is to first include standard library headers (<iostream>), then third-party libraries (<boost/thread.hpp>) and finally local headers (“FizzBuzz.hpp”). While this might be the recommended way of doing it in for instance Python, it is not the best way to do it in C++. A colleague of mine just went through a lot of pain when swapping out a big library in their codebase. Why was that?
Imagine you are writing a library that depends on some other library, like the STL (hardly any library doesn’t). Imagine you are a good boy (or girl) and write the test first. You might do something like this:
#include <vector> //STL #include <gtest/gtest.h> //ThirdParty #include "geometry.hpp" //In-house #define PI 3.2 //As pr. Indiana Bill #246, 1897 TEST(TestGeometry, rotatingOrigoGivesOrigo) { std::vector<double> v(2,0); rotate(v, PI); EXPECT_EQ(0, v[0]) << "X got moved!"; EXPECT_EQ(0, v[1]) << "Y got moved!"; }
and your geometry.hpp
looks like this:
void rotate(std::vector<double>& v, double angle);
This all works out nicely until someone else wants to include geometry.hpp
without including <vector>
first, and get something like geometry.hpp:1: error: ‘vector’ is not a member of ‘std’
. You have now forced all the users of your geometry library to include <vector>
before including geometry.hpp
.
While this can seem like a trivial example, this stuff quickly grows a lot hairier when you have a larger codebase with lots of dependencies. A header file A might depend on header B which depends on C, which declares types you have never heard of, and rightly so. And when you try to compile, you get error: ‘würkelschmeck’ is not a member of ‘std’
. Or something a lot worse if templates are involved.
My suggested rule is:
- Local headers
- Third party headers
- STL headers
If you enjoyed this post, you can subscribe to my blog, or follow me on Twitter
Good point. I’ll add one additional rule – “Include yourself first”. E.g. The first include in hello.cpp should be hello.h. That way you don’t get into the same problem within your project files.
Of course this rule doesn’t really apply in unit tests where there is no definition/implementation separation.
Very good point Mike.
Seems like “C++ on a Friday” became “C++ on a Thursday” this week. I need to work on my calendar skills.
But why does it work if I include in your suggested order?
If your header file
my.hxx
needs to haveiostream
included in order to work, but doesn’t include it,my.cc
will still compile if it includesiostream
beforemy.hxx
. You might then miss the fact thatmy.hxx
is missing#include <iostream>
.This problem will not become apparent until you try to include
my.hxx
in another file at a later time. If you include in my suggested order, you will be aware of it at once, and need to fix it.Nice to see others using the same practices as me! One of the little details that *should* be commonplace in C++ software, but sadly usually isn’t.
(By the way, you’ve misspelled in “A very common one I think is to first include standard library headers ()”, at the top.)
I agree, this should definitely be commonplace.
And thanks for the heads up!
But if geomery.hpp refers to std::vector then it should have an #include in it, to be self sufficient? (Or could even be forward declared in C++11)
Yeah that’s kind of the point I’m trying to make. If you have the proper include order in your test, you can’t make the error of forgetting to include vector in geometry.hpp.
no you are missing the point of what he said entirely. If geometry.hpp is using vector like that then it ( geometry.hpp) should be including vector itself. geometry.hpp is broken, that’s the problem. Not the order of includes in your test. if you use a syntax-only compile check of your headers you’d discover this. the include order doesnt neccessarily protect you from anything. what if another project include file is included before geometry that include vector? you’re back to square one.
Thank you, würkelschmeck!