11

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.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • 1
    You can leave out the definition in your module interface (`helloworld.cpp`) and only include it in your module implementation. If that actually achieves the exact result you are looking for I can't say, but it seems reasonable it would work that way. Nothing from the module implementation is exported. – super Apr 30 '20 at 10:43
  • @super this is a possibility, I was hoping though that modules would offer a better way without requiring me to split implementation and headers like that (notably, for an external project to also use the module, I would need three files like before modules: interface, implementation and instantiation as in "Remove definitions from included headers but also expose templates an external API" of https://stackoverflow.com/a/59614755/895245 ) – Ciro Santilli OurBigBook.com Apr 30 '20 at 12:03
  • If you want the template definition exposed but at the same time get the gains of not having to instatiate it for a certain type, all you need to do is add an explicit instatiation after the definition. No need to split anything. If you want to hide the definition but instatiate it for certain types you just need to split it in interface/implementation. I not following your reasoning... – super Apr 30 '20 at 13:40
  • I can't say for sure because modules are so new, but it seems to me that with modules you don't need explicit instantiation to gain the compile time speed boost. The point of the modules is that the source code for the module will be processed only once and stored in a compiler-friendly AST for fast reusability. If I had to guess I would venture that template instantiations get the same treatment, i.e. when you call a `hello` it's definition will be stored once and reused. Again, it's just an educated speculation from my part from all the talks on modules I've watched. – bolov Apr 30 '20 at 14:50
  • @bolov: The compilation advantages of modules is that you don't need to parse the text of a template every time you import it. But instantiation is a separate matter entirely, and for complex metaprogramming, that can still be slow. And you can still instantiate the same template in different translation units; modules doesn't change that. – Nicol Bolas Apr 30 '20 at 15:38

1 Answers1

3

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.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76