Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternative to "extern template" in every source file

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.

like image 617
Truman Ellis Avatar asked Apr 23 '15 16:04

Truman Ellis


People also ask

How to reduce compile times by using extern templates?

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.

What is an extern template in C++11?

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.

Is Ultrasearch a good alternative to everything?

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?

What is the best alternative to everything?

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.


2 Answers

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.

like image 147
Angew is no longer proud of SO Avatar answered Sep 21 '22 02:09

Angew is no longer proud of SO


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.

like image 33
5gon12eder Avatar answered Sep 21 '22 02:09

5gon12eder