Tips and tricks » Speeding up compilation

Techniques for reducing compilation times used by Magnum itself and recommended for application code as well.

Forward declarations instead of includes

An essential thing when speeding up compilation is reducing number of #include directives in both headers and source files. Magnum is strictly applying this policy in all header files, so all types which are not directly used in the header have only forward declarations.

For example, when including Magnum.h, you get shortcut typedefs for floating-point vectors and matrices like Vector3 and Matrix4, but to actually use any of them, you have to include the respective header, e.g. Magnum/Math/Vector3.h.

You are encouraged to use forward declarations in your code as well. However, for some types it can be too cumbersome — e.g. too many template parameters, default template arguments, typedefs etc. Instead, forward declaration headers are available, with each namespace having its own:

Templates

Many things in Magnum are templated to allow handling of various types and sizes of data, for example whole scene graph can operate either with Float or Double data type. However, having templated classes and function usually means that the compiler compiles the whole templated code again in each compilation unit (i.e. source file). In linking stage of the application or library the duplicates are just thrown out, which is a waste of compilation time. A few techniques are employed in Magnum to avoid this.

Template headers and implementation files

When templated code is too large, it is not stored in header file, but in so-called template implementation file. Generally, all header files in Magnum have *.h extension and all source files have *.cpp extension. Template implementation files have *.hpp extension, hinting that they are something between *.h and *.cpp files.

Template implementation file can be included along the header itself and it will just work, but it will negatively affect compilation time. If you are using one template specialization in many places, the compiler performs compilation of the same template specialization many times, as said above. Template implementation files give you the ability to explicitly instantiate the template only once in some dedicated source file. Then you can include just the header everywhere else and leave the rest on the linker.

Templated classes having code in template implementation files state in their documentation all common specializations that are already compiled in the libraries. So, unless the templated class is too generic or you need something special, you don't have to mess with template implementation files at all. See SceneGraph::Object or SceneGraph::Camera for an example.

Sometimes, however, you need to use your own specialization and that's why template implementation files are installed along with the library. For example we want to use Object from SceneGraph with BasicMatrixTransformation3D with Double instead of Float as underlying type, because our scene will span the whole universe. We include the implementation file in dedicated source file and explicitly instantiate the template:

// Object.cpp
#include "SceneGraph/Object.hpp"
#include "SceneGraph/MatrixTransformation3D.h"

using namespace Magnum;

template class SceneGraph::Object<SceneGraph::BasicMatrixTransformation3D<Double>>;

All other files using the same object specialization now need to include only SceneGraph/Object.h header. Thus the Object specialization will be compiled only once in our Object.cpp file, saving precious compilation time.

Extern templates

Keyword extern template is a new thing in C++11, attempting to solve compilation time problems related to templated code. However, on some compilers it causes conflicting symbol errors when used on whole classes, thus in Magnum it's used only for specific functions.

This is completely transparent to end user, so no special care is needed. Extern template is used for example for debug operators for common types of matrices and vectors.