Explicit Instantiation

Drastically reduce recompilation time of template-heavy C++ code by instantiating user-specified time-consuming templates in separate compilation units. This mechanism is a pure CMake optimisation and completely unintrusive for the C++ code!

Example

Add the module path to the top level CMakeLists.txt...

1 list(APPEND CMAKE_MODULE_PATH /path/to/cmakescripts)

...such that this include works

1 include(fsc/explicit_inst)

One can now instantiate a template (A) with the desired types (int, char) from a header (A.hpp) for some targets (example1, example2)

1 explicit_inst("example1 example2" foo/A.hpp A<int> A<char>)

Arguments

Lets assume we have the following file structure and the examples use A.hpp that contains a very complex template A that takes a long time to compile:

├── CMakeLists.txt
├── example
│   ├── CMakeLists.txt
│   ├── example1.cpp
│   └── example2.cpp
└── src
    └── foo
        └── A.hpp

Targets

The first argument is the target. If multiple targets are supplied, they have to be passed in quotes!

1 explicit_inst( example1 ...) # ok
2 explicit_inst("example1" ...) # ok
3 explicit_inst("example1 example2" ...) # ok
4 explicit_inst( example1 example2 ...) # bad! will not work

Source

The second argument is the source, that contains the definition of the template, and can be specified

absolute:

1 explicit_inst(... /abs_path_to_project/src/foo/A.hpp ...)

relative (we assume here that this line is in example/CMakeLists.txt):

1 explicit_inst(... ../src/foo/A.hpp ...)

via include_directories:

1 include_directory(${PROJECT_SOURCE_DIR}/src)
2 ...
3 explicit_inst(... foo/A.hpp ...)

The last method allows for file movement (as long as they are still included the same way) without having to change the explicit_inst input. I.e. one only needs to change it if the includes in the source (#include <foo/A.hpp>) need to be changed as well.

Instances

All arguments that follow are instances. They have to be specified with namespace!

1 include_directory(${PROJECT_SOURCE_DIR}/src)
2 ...
3 explicit_inst(... A<int>)
4 explicit_inst(... A<int> A<float>)
5 explicit_inst(... std::vector<int>)

(the last example only works if you know where the exact source file is)

How it works

The downside to templates regarding compile-time is the fact, that each compilation unit instantiates maybe the same template and the linker removes all but one instantiation...

To prevent this from happening, we let each compile unit know, that there will be an instantiation provided while linking with, such that it does not instantiate:

extern template class A<int>;

We add another compilation unit (A_int.cpp which is generated automatic):

#include<foo/A.hpp>
template class A<int>;

That explicitly instantiates the template. Now only this unit takes up compile-time for A<int>. We only have to link to the original target.