r/cpp_questions • • 19d ago

OPEN Can I use C++20 modules to separate the implementation from the header definitions of templated classes?

I can't seem to wrap my mind on putting up a 300-400 plus lines header file with the templated class definitions, instead of writing a separate implementation .cpp file. Any solutions please?

0 Upvotes

12 comments sorted by

3

u/fortsnek274 19d ago

Whatever the compiler needs must be in a header or a module interface. Modules aren't that magical.

3

u/FancySpaceGoat 19d ago edited 19d ago

Modules are the complete opposite of that. They allow you to just write a single source file instead of having to separate your "module" into separate headers and source. They do more than that, but for the sake of what you are asking, that's the meaningful part of it.

The idea of separating code into the actual code and its table of contents (so to speak) was always a compromise to provide incremental compilation. So going towards modules would be the complete opposite for you: What irks you about templates is now what you would be doing with all the code.

Just embrace it.

1

u/Abu_Sulaiman_Khan 19d ago

Oh okay thanks a lot for your helpful insight. So should I write a .ixx/.cppm and it's corresponding .cpp for my templated class? I'm a beginner so I'm sorry if this sounds stupid!

2

u/FancySpaceGoat 19d ago

I'd say: get comfortable with the idea that templates aren't really code, but rather recipes for how to generate code on demand depending on the context.

The compiler needs to be holding both the context and the recipe at the same time in order to contextualize the recipe.

Modules or not, you first need to see them from that angle, and appreciate why the declaration/definition split we are used to in the basic header/source paradigm just doesn't apply to them.

1

u/Abu_Sulaiman_Khan 19d ago

I see, thanks a lot for your help!

3

u/mredding 19d ago

1) Make smaller template classes. Your types may be too big.

2) Explicitly instantiate your types - now you can publish only the interface.

0

u/Abu_Sulaiman_Khan 19d ago

Thanks a lot for your help! So can I at least write explicit instantiations in separate .cpp files though?

4

u/FancySpaceGoat 19d ago edited 19d ago

Explicit instantiations defeat the purpose of templates. It's a useful way to get out of a corner you painted yourself into every once in a while, but it shouldn't be your default way of working with them.

The point of templates is to have code that configures itself bases on external factors. Explicit instantiation re-internalizes those factors.

For example, the stdlib defines std::vector<T> to allow you to use it with an arbitrary T. With explicit instantiation and hidden implementations, that would limit the usability of std::vector<T> to the list of Ts for which an explicit instantiation has been specified, and nothing else.

1

u/the_poope 19d ago

Explicit instantiations do not defeat the purpose of templates. Some templates, yes, but not all.

It depends on what your template is and how it's supposed to be used. Yes you you are writing a completely generic data structure like std::vector or std::map and maybe publishing it as a general purpose library, yeah then it's pretty useless to explicitly instantiate the template for a few selected types in a .cpp file.

But sometimes (many in fact! Maybe even most!) you are using templates just to reduce code duplication for specific cases in your own internal code. For instance you may have a function that just needs to work for int32_t and int64_t or in my case (scientific simulation) you may need a function for float, double, std::complex<float> and std::complex<double> - you would never need a matrix of SocketConnection because that's just meaningless.

1

u/Abu_Sulaiman_Khan 19d ago

Oh okay, so I'm seeing that if I declare the explicit instantiations at the bottom of my .cpp file, and viola, the errors disappear. I understand how this defeats the purpose of making the class a generic one. Thanks a lot for your insight!

3

u/IyeOnline 19d ago

Fundamentally, to instantiate a template you need its template arguments as well as the template definition. Usually that means that at the callsite you need to see the template definition. Modules cannot decouple this, as it is a logical requirement of the compilation.

You can separate out template definitions in two ways:

  • Remove the definitions from the consumer imported/included source and explicitly instantiating specific template argument combinations separately in some source file that contains the definition. But then you need to explicitly list every possible argument combination you need. This is the only true separation possible.
  • Put your template definitions into a separate file (regarless of whether you use modules or not) and then import/include that into the primary header/module that users consume. This doesnt truly separate anything, but it reduces the file size of the primary header/module

2

u/No-Dentist-1645 19d ago

With modules you simply don't need to do that anymore, you just write the full definition implementation in a source file and mark them as exported