Why Use a "Tpp" File When Implementing Templated Functions and Classes Defined in a Header

Why use a tpp file when implementing templated functions and classes defined in a header?

Does it matter if it's .tpp or any other extension? And why not use a .cpp?

It does not matter what the extension is, but don't use .cpp because it goes against conventions (it will still work, but don't do it; .cpp files are generally source files). Other than that it's a matter of what your codebase uses. For example I (and the Boost codebase) use .ipp for this purpose.

What's the significance of a .tpp file?

It's used when you don't want the file that contains the interface of a module to contain all the gory implementation details. But you cannot write the implementation in a .cpp file because it's a template. So you do the best you can (not considering explicit instantiations and the like). For example

Something.hpp

#pragma once

namespace space {

template <typename Type>
class Something {
public:
void some_interface();
};

} // namespace space

#include "Something.ipp"

Something.ipp

#pragma once

namespace space {

template <typename Type>
void Something<Type>::some_interface() {
// the implementation
}

} // namespace space

I thought the whole point of writing definitions in headers and the implementations in a separate file is to save compilation time, so that you compile the implementations only once until you make some changes

You can't split up general template code into an implementation file. You need the full code visible in order to use the template, that's why you need to put everything in the header file. For more see Why can templates only be implemented in the header file?

But if the implementation file has some funky looking file extension, how does that work in terms of compiling? Is it as efficient as if the implementations were in a cpp?

You don't compile the .tpp, .ipp, -inl.h, etc files. They are just like header files, except that they are only included by other header files. You only compile source (.cpp, .cc) files.

Why can templates only be implemented in the header file?

Caveat: It is not necessary to put the implementation in the header file, see the alternative solution at the end of this answer.

Anyway, the reason your code is failing is that, when instantiating a template, the compiler creates a new class with the given template argument. For example:

template<typename T>
struct Foo
{
T bar;
void doSomething(T param) {/* do stuff using T */}
};

// somewhere in a .cpp
Foo<int> f;

When reading this line, the compiler will create a new class (let's call it FooInt), which is equivalent to the following:

struct FooInt
{
int bar;
void doSomething(int param) {/* do stuff using int */}
}

Consequently, the compiler needs to have access to the implementation of the methods, to instantiate them with the template argument (in this case int). If these implementations were not in the header, they wouldn't be accessible, and therefore the compiler wouldn't be able to instantiate the template.

A common solution to this is to write the template declaration in a header file, then implement the class in an implementation file (for example .tpp), and include this implementation file at the end of the header.

Foo.h

template <typename T>
struct Foo
{
void doSomething(T param);
};

#include "Foo.tpp"

Foo.tpp

template <typename T>
void Foo<T>::doSomething(T param)
{
//implementation
}

This way, implementation is still separated from declaration, but is accessible to the compiler.

Alternative solution

Another solution is to keep the implementation separated, and explicitly instantiate all the template instances you'll need:

Foo.h

// no implementation
template <typename T> struct Foo { ... };

Foo.cpp

// implementation of Foo's methods

// explicit instantiations
template class Foo<int>;
template class Foo<float>;
// You will only be able to use Foo with int or float

If my explanation isn't clear enough, you can have a look at the C++ Super-FAQ on this subject.

Template (.tpp) file include guards

Do I need include guards in the .tpp file or is it sufficient to have them in the .hpp file?

Include guards are never needed: they're just terribly useful, cheap, non-disruptive and expected. So Yes, you should protect both files with header guards:

  • Terribly useful: they allow you to declare a dependency from multiple files without keeping track of which files have already been included.
  • Cheap: this is just some precompilation tokens.
  • Non-disruptive: they fit well with most use-cases of #include (I've had a colleague who didn't know how to write macros so he #included implementation files facepalm).
  • Expected: developers know what they are and barely notice them; on the contrary a header file missing include guards wakes us up and adds to the global wtf/line counter.

I take the opportunity to highlight the comment from StoryTeller:

I'd go a step further and add a descriptive #error directive if the hpp guard is not defined. Just to offer a little protection from people including the tpp first.

Which will translate to:

#ifndef MYCLASS_TPP
#define MYCLASS_TPP

#ifndef MYCLASS_HPP
#error __FILE__ should only be included from myclass.hpp.
#endif // MYCLASS_HPP

template<typename T>
T MyClass<T>::foo(T obj)
{
return obj;
}

#endif // MYCLASS_TPP

Notice: if a translation unit first #include <myclass.hpp> and then #include <myclass.tpp>, no error is fired and everything is fine.

Declaring a template member function in a tpp file

Are you giving the compiler the file "A.tpp", or another C++ source file that includes "A.h"? The way you have this structured, I would think the latter would give you the behavior you expect. Apologies, I don't have much VS experience.

Templated Classes using header and source file

What is the proper way to do this with the implementation include at the bottom of the header file?

Put the include guards into your header file, including the implementation #include directive:

#ifndef __FOO_H
#define __FOO_H
// Foo.h
template <typename T>
struct Foo
{
void doSomething(T param);
};

#include "Foo.tpp"

#endif

You may also add the guards to Foo.tpp, but in the situation you posted it will not make much sense.

Would the include guards on my .cpp file prevent the compiler from creating the templates class/function for other types since it can now only be included once?

Typically you don't need include guards in *.cpp files at all, as you don't include them anywhere. Include guards are only needed in those files which are included into multiple translation units. And of course, those guards will not prevent instantiating the templates for other types, since it is what templates are designed for.

Isn't part of the reason for using header files in the first place is to prevent code from being recopied every time it is included, to keep a short compilation time? So what is the performance effect of templated functions (since they have to be defined in header) versus simply overloading a function/class? When should each be used?

Here you raise a big, platform-dependent and a bit opinion-based topic. Historically, include files were used to prevent code copying, as you said. It was enough to include function declarations (headers, not definitions) into multiple translation units, and then link them with a single copy of the compiled code for included functions.

Templates compile much slower than non-template functions, so implementing template export (separate header/implementation compilation for templates) isn't worth saving compilation time.

For some discussions and good answers on template performance, check these questions:

  • Is Template Metaprogramming faster than the equivalent C code?
  • C++ templates for performance?
  • Do c++ templates make programs slow?

In short, sometimes templates allow you to make some decisions in compile time instead of runtime, making the code faster. Anyway, the only proper way to determine if the code became faster or not, is to run performance tests in real-world environment.

Finally, templates are more about design, not about performance. They allow you to significantly reduce code duplication and conform DRY principle. A banal example of it are functions like std::max. A more interesting example is Boost.Spirit, which uses templates for building parsers entirely in compile time.

Testing templated classes with functions defined in CPP file

A variation of solution #1 by renaming the files:

  • Foo.h
#pragma once // or/and header guards
<template class Bar>
class Foo
{
public:
Bar bar;
bool ExampleFunction();
};
  • Foo.inl (or other extension .inc, .ixx, ...)
#pragma once // or/and header guards
#include "Foo.h"
template <class Bar>
bool Foo<Bar>::ExampleFunction()
{
return bar.Func() > 10;
}
  • Foo.cpp
#include "Foo.h"
#include "Foo.inc"
#include "Bar.h"

// explicit instantiation
template <> struct Foo<Bar>;
  • FooTest.cpp
#include "Foo.h"
#include "Foo.inc"
#include "BarMock.h"

// Testing code...

Elegant solution to implementing c++ templates

Why is it so?
As templates compiles through two phases in first phase compiler checks mostly for syntactical errors. If there is no error found in your template is legal to be used, but at this stage compiler do not generate any code for it. And in the second phase compiler will generate the code for all the class members function, of templated functions you used.

Because templates are evaluated at compile time. So what happens when compiler compiles it? For example if you defined a template in templated.hpp file and its implementation in implementation.cpp file. Compiler compiles each file separately into an object and then linker link them together. As templates are evaluated at compile time so compiler need its implementation at compile time, which is not available if you are having it in different implementation file. So linkers complains to you that I could not find implementation for type T for your this template. This all happens at compile time.

So far until C++20 or even C++23 templates are still needed to be evaluated at compile time albeit C++ has added new concept modules, I am not sure it can be used this way, but you can read about it here.

If I've separated a template into a header and source, is there any way to compile it to its own object file?

Is there no way at all around this?

Yes, there is. If you instantiate a template explicitly in the translation unit where the functions are defined, then you can use those instances in other translation units.

But that of course limits what template arguments can be used to those that you've chosen for explicit instantiation. For unconstrained template arguments, there's no way around defining the functions in all translation units (where they are ODR-used).



Related Topics



Leave a reply



Submit