Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use template explicit instantiation with C++20 modules?

As explained in this answer template instantiation allows reducing compilation times and sizes by not requiring templates to be recompiled for every new type in every new file that uses them.

I'm also excited about how C++20 modules should provide a clean solution to expose templates to external projects and reduce hpp/cpp duplication.

What is the syntax that will allow them to work together?

For example, I expect modules to look a bit like (untested and therefore likely wrong code because I don't have a compiler new enough/not sure it is implemented yet):

helloworld.cpp

export module helloworld;
import <iostream>;

template<class T>
export void hello(T t) {
    std::cout << t << std::end;
}

helloworld_impl.cpp

export module helloworld_impl;
import helloworld;

// Explicit instantiation
template class hello<int>;

main.cpp

// How to prevent the full definition from being imported here, which would lead
// hello(1) to instantiate a new `hello<int>` instead of reusing the explicit instantiated
// one from `helloworld_impl.cpp`?
import helloworld;

int main() {
    hello(1);
}

and then compilation mentioned at https://quuxplusone.github.io/blog/2019/11/07/modular-hello-world will be along (?)

clang++ -std=c++2a -c helloworld.cpp -Xclang -emit-module-interface -o helloworld.pcm
clang++ -std=c++2a -c -fprebuilt-module-path=. -o helloworld_impl.o helloworld_impl.cpp
clang++ -std=c++2a -fprebuilt-module-path=. -o main.out main.cpp helloworld_impl.o

Ideally, I also want the template definition to be usable on external projects.

I think what I want is a way to import the module, and at import time decide between either:

  • use all templates in the module as if they were declarations only (I'll be providing my own instantiations on another file)
  • use the templates in the module as if they were definitions

This is basically what I achieve in pre-C++20 at "Remove definitions from included headers but also expose templates an external API" but that setup requires copying the interfaces twice, which seems like something the module system can essentially do for us.


People also ask

How do I force a template instantiation?

To instantiate a template function explicitly, follow the template keyword by a declaration (not definition) for the function, with the function identifier followed by the template arguments. template float twice<float>(float original); Template arguments may be omitted when the compiler can infer them.

How many times is the template class instantiated?

Template instantiation has two forms: explicit instantiation and implicit instantiation.

How the instantiation of function template happens?

When a function template is first called for each type, the compiler creates an instantiation. Each instantiation is a version of the templated function specialized for the type. This instantiation will be called every time the function is used for the type.

Why we need to instantiate the template?

Class template instantiationIn order for any code to appear, a template must be instantiated: the template arguments must be provided so that the compiler can generate an actual class (or function, from a function template).


1 Answers

Modules make the “fast single build” case very easy. They don’t do much for the “support client instantiation but avoid rebuilding clients of the explicitly instantiated specializations” case; the theory is that the generally faster builds from avoiding repeated work makes it unnecessary to contort the program so as to save even more time.

All you do is put an explicit instantiation definition in the module interface:

export module A;
export template<class T>
inline void f(T &t) {++t;}
template void f(int&);
template void f(int*&);

Importers will not have to instantiate f for either of those two types, even though the function template is inline (which may require additional instantiations in non-modular code). A typical implementation caches the results of those instantiations in the compiled module interface file with sufficient detail to inline calls in an importer (as well as caching the template itself with sufficient detail to instantiate it further).

You can of course also use an explicit instantiation declaration with just a declaration of the template in the interface and define the template and put explicit instantiation definitions in a module implementation unit, but that’s no different from how header files work.

like image 176
Davis Herring Avatar answered Sep 25 '22 05:09

Davis Herring