I'm working on a library where many of our core objects are templates with one particular instantiation showing up in most of the files in the project in the form of a smart pointer to that template instantiation. I explicitly instantiate these templates in a single source file. We recently switched to C++11 and I'm trying to use the new
extern template class MyTemplate<double>;
to speed up the compilation. My first question is whether my use of smart pointers around
MyTemplate<double>
is implicitly instantiating the template and requires the "extern template .." at the top of the file to avoid duplicate instantiation.
My second question is whether there is some alternative to adding all these
extern template class MyTemplate<double>;
to every source file. It just gets a little tedious grepping every instance of the smart pointer for every template I define and making sure I have the correct "extern template" line in that file. I can also see it being a little difficult to enforce this convention for future developers of our code as they may add a template instantiation and forget the appropriate extern template line, especially since no error will be generated.
Reducing compile times by using extern template is a project scope strategy. One should consider which are the templates most expensive that are used in many translation units and find a way to tell the build system to compile it only one time. But let’s consider for a moment what we’ve done in the previous paragraph.
In C++11, extern template feature is introduced to avoid this situation. Above will tell the compiler to not instantiate template function or class as it is being instantiated somewhere else. Programmer should use this if and only if above template function or class is instantiated somewhere else.
UltraSearch searches files and folders on local NTFS drives and provides the results within just a few seconds. Almost everyone thinks UltraSearch is a great alternative to Everything. Is this a good alternative?
Almost everyone thinks FSearch is a great alternative to Everything. gdx FSearch is very fast, with an intuitive graphical interface. It's not an exact clone of Everything but it's very similar, taking in consideration the differences between Windows and Linux.
If you know for certain that you'll be explicitly instantiating the template, simply put the explicit instantiation declaration (the extern template
line) into the header, so it gets included with the template.
There is no problem with this line being present in the instantiating file—the standard explicitly allows it, as long as the explicit instantiation definition (the non-extern
one) comes after the explicit instantiation declaration (the extern
one). The ruling is in C++14 14.7.2/11; the same ruling is present in C++11 too.
You can put the extern template
declarations right into your header file where the template
is defined. For example:
In file useful.hxx
:
#ifndef USEFUL_HXX
#define USEFUL_HXX
namespace my
{
template <typename T>
T
do_useful_stuff(T x)
{
return x;
}
extern template int do_useful_stuff(int);
extern template float do_useful_stuff(float);
// Potentially many more...
} // namespace my
#endif // ifndef USEFUL_HXX
In file useful.cxx
:
#include "useful.hxx"
namespace my
{
template int do_useful_stuff(int);
template float do_useful_stuff(float);
// Potentially many more...
} // namspace my
And finally in file main.cxx
:
#include <iostream>
#include "useful.hxx"
int
main()
{
std::cout << my::do_useful_stuff(42) << std::endl;
std::cout << my::do_useful_stuff(1.0f) << std::endl;
}
You can now compile useful.cxx
and main.cxx
and then link both together.
So far so good. However, as you can see, this is still quite repetitive. If you're not afraid of some preprocessor magic, you can factor the common stuff out into a file useful.txx
#ifndef MY_EXTERN
#error "Please '#define MY_EXTERN' to 'extern' or '' before '#include'ing this file."
#endif
namespace my
{
MY_EXTERN template int do_useful_stuff(int);
MY_EXTERN template float do_useful_stuff(float);
// Potentially many more...
} // namspace my
and then #include
it in useful.cxx
#include "useful.hxx"
#define MY_EXTERN /* empty*/
#include "useful.txx"
#undef MY_EXTERN
and useful.hxx
#ifndef USEFUL_HXX
#define USEFUL_HXX
#ifndef MY_USE_EXTERN_TEMPLATES
#define MY_USE_EXTERN_TEMPLATES 0
#endif
namespace my
{
template <typename T>
T
do_useful_stuff(T x)
{
return x;
}
} // namespace my
#if MY_USE_EXTERN_TEMPLATES
#define MY_EXTERN extern
#include "useful.txx"
#undef MY_EXTERN
#endif
#endif // ifndef USEFUL_HXX
like shown. Note that I've also used the opportunity to make the extern
declarations conditional so clients can use your header file with either instantiation model. Therefore, we update the main.cxx
as well:
#define MY_USE_EXTERN_TEMPLATES 1
#include <iostream>
#include "useful.hxx"
int
main()
{
std::cout << my::do_useful_stuff(42) << std::endl;
std::cout << my::do_useful_stuff(1.0f) << std::endl;
}
And again, we can compile useful.cxx
and main.cxx
and link the resulting object files together. If we don't #define MY_USE_EXTERN_TEMPLATES
to 1 in main.cxx
, the file could be compiled and linked in one flush.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With