You know the drill: in C++ you need to #include header files that declare the types and functions from the libraries that you use. Qt is no exception in this regard. Qt follows a very nice convention for its own datatypes:
if you need to use a type called QType
,
then use #include <QType>
.
This is very nice, as you don't need to guess the header name! Just include exactly the type that you want to use. You need QUrl? #include <QUrl>
. You need QQmlComponent? #include <QQmlComponent>
. Easy peasy.
(It's extremely nicer, than, say, the organization of the headers in C++ Standard Library -- quick quiz, which header do you need to include in order to use std::packaged_task
?)
If you want to avoid clashes, you can even be more explicit: for a type QType in the module QtModule you can use #include <QtModule/QType>
; that is, prefix the header name with the module it belongs to. You can find the module's name in the documentation of each datatype in Qt. The examples above would then be written as #include <QtCore/QUrl>
and #include <QtQml/QQmlComponent>
, respectively.
There is even a third way: you can simply use #include <QtModule>
, and this will include all the headers in that Qt module.
Wait, won't that include a lot of headers?
That is absolutely correct. And to be pedantic, such an inclusion won't simply include all the headers in that Qt module; it will also include all the headers from any module that module depends on!
If you peek for instance into the QtQuick
header, one finds this:
Line 3 includes, transitively, all the headers in the dependencies of the QtQuick module:
In turn, including QtQml will include QtNetwork's headers, and so on. You can easily end up including thousands of header files! This will make your compilation times skyrocket.
Surely you can't be serious?
I am serious, and don't call me Shirley The module-wide header inclusion was introduced for a good reason: to exploit precompiled headers (PCH). Precompiled headers are a way to speed up compilation by having the compiler parse some headers and save them in a (binary) format, which is much faster for the compiler to reload later. When compiling some other file, the compiler will then load the precompiled header file and save time (compared to re-parsing the headers).
The theory goes like this: Qt can be built with support for precompiled headers (e.g. with the configure's -pch
command line option). In such a setup, each Qt module should generate a precompiled header, and using the module-wide inclusion (and only the module-wide inclusion!) would then use the precompiled header for that module, resulting in a significant speedup when compiling a project.
This actually is not the case! Support for precompiled headers does exist in Qt, but it's only for Qt's internal consumption (when Qt itself gets built). Projects using Qt won't find any precompiled headers for them to use. So, actually using the module-wide inclusion is going to be dreadful!
The QtTest fiasco
Now, I shouldn't be here telling you this story -- it's very rare to actually encounter such module-wide includes in "ordinary" software using Qt (which rightfully follows "include what you use", and other similar good coding policies).
There's, however, a major offender found all over the place: the Qt Test module and its module-wide inclusion. It's extremely easy to misspell the right inclusion for QtTest functions, which results in the wrong inclusion getting used everywhere because, alas, test code is often copied and pasted.
The wrong way to include QtTest is:
Yes, the difference is just a little 't' that may slip in when typing. That little difference, on a codebase that has a significant amount of tests, has a noticeable impact.
For instance, Qt's own auto-tests are plagued by the QtTest module-wide inclusion; in qtbase alone there are ~580 occurrences of #include <QtTest>
in 775 C++ source files, about 75%. After a search and replace to fix these directives and some rounds on the CI to manually re-add all the includes statements (that were accidentally added transitively), the build times for the Qt test suite dropped by ~15%! Assuming a mostly CPU-bound build, that's a 15% power save.
Sure, it's still dwarfed by the running time of the tests (Qt has lots of GUI tests, which cannot be easily parallelized), but do your part, fix the QtTest includes today, and save energy!
Thanks for reading.
Trusted software excellence across embedded and desktop platforms
The KDAB Group is a globally recognized provider for software consulting, development and training, specializing in embedded devices and complex cross-platform desktop applications. In addition to being leading experts in Qt, C++ and 3D technologies for over two decades, KDAB provides deep expertise across the stack, including Linux, Rust and modern UI frameworks. With 100+ employees from 20 countries and offices in Sweden, Germany, USA, France and UK, we serve clients around the world.
2 Comments
20 - Jan - 2021
kpop
In our codebase, replacing module includes with 'normal' ones resulted in a 5% faster build. Not as impressive as the 15% mentioned here, but still very welcome. I was always looking to reduce our own code, but once you look at the preprocessed source, what is actually compiled ( -save-temps), it's clear that includes make up most of it code. Thank you mentioning this!
21 - Jan - 2021
Giuseppe D'Angelo
Hi, Glad that it helped. A further step you might want to evaluate is using precompiled headers; with a reasonably recent CMake, it's actually super easy, and that's where you might want to resort to use module-wide include again (to generate the precompiled header).