How can I use explicit template creation for the template member functions defined in the class definition?

In order to reduce compilation time in a large project that uses liberal use of templates, I had good results using the extern template ( explicit template creation ) to prevent the use of common template functions in many different compilation units.

However, one of the unpleasant things is that it does not work for the member functions defined in the class definition.

For example, I have the following class of templates:

template <typename T> struct Foo { static T doubleIt(T input) { return input * 2; } }; 

Now I know that Foo is most often used for numeric types, so I add this to the header:

 extern template struct Foo<int>; extern template struct Foo<float>; extern template struct Foo<double>; 

And in the cpp file add explicit instances:

 template struct Foo<int>; template struct Foo<float>; template struct Foo<double>; 

This does not work, since dumpbin.exe in the obj file tells me:

 017 00000000 SECT4 notype () External | ?doubleIt@ ?$Foo@M @@ SAMM@Z (public: static float __cdecl Foo<float>::doubleIt(float)) 

If I changed the class definition to define a function outside the class header, so that it works correctly:

 template <typename T> struct Foo { static T doubleIt(T input); }; template <typename T> T Foo::doubleIt(T input) { return input * 2; } 

What we can check with dumpbin:

 017 00000000 UNDEF notype () External | ?doubleIt@ ?$Foo@M @@ SAMM@Z (public: static float __cdecl Foo<float>::doubleIt(float)) 

The problem with this solution is that there is a lot of need to move all function definitions outside the class definition, especially when you get more template parameters.

I tried using declspec (__ noinline), but it still does not execute functions correctly (and prevents unwanted inlining of the function, if possible).

One thing that works is to list each function individually, for example, but this, of course, is even more cumbersome:

 extern template int Foo<int>::doubleIt(int); extern template float Foo<float>::doubleIt(float); extern template double Foo<double>::doubleIt(double); 

What I would like is a way to keep the function definition inside the class definition, while retaining the ability to be inline, if possible, but when it is not inline, only create it in the compilation unit, where it is explicit (in other words, exactly the same behavior, like moving a function outside a class definition).

+7
c ++ templates inline
source share
2 answers

You cannot have this in both directions to embed the method that the compiler should use to use the source code, since the method is defined in a line, the compiler does not interfere with compiling it into an object file if it is not used directly in this object (and even if so, if its built-in in all cases it will not be present in the object as a separate method). The compiler will always create your function, if it is defined in the header, somehow compelling the compiler to store a copy of this function in an object file will not improve performance.

+2
source share

As already mentioned, you cannot have both extern and inlining, but about the extra part of the input, I did something similar and tried to minimize its use of the preprocessor. I'm not sure that you will find this useful, but just in case, I will give an example with a template class that has a template function inside.

File Foo.h :

 template<typename T1> struct Foo { void bar(T1 input) { // ... } template<typename T2> void baz(T1 input1, T2 input2); }; #include <Foo.inl> 

File Foo.cc :

 template<typename T1> template<typename T2> void Foo<T1>::baz(T1 input1, T2 input2) { // ... } #define __FOO_IMPL #include <Foo.inl> #undef __FOO_IMPL 

File Foo.inl :

 #ifdef __FOO_IMPL #define __FOO_EXTERN #else #define __FOO_EXTERN extern #endif #define __FOO_BAZ_INST(T1, T2) \ __FOO_EXTERN template void Foo<T1>::baz<T2>(T1, T2); #define __FOO_INST(T1) \ __FOO_EXTERN template struct Foo<T1>; \ __FOO_BAZ_INST(T1, int) \ __FOO_BAZ_INST(T1, float) \ __FOO_BAZ_INST(T1, double) \ __FOO_INST(int) __FOO_INST(float) __FOO_INST(double) #undef __FOO_INST #undef __FOO_BAZ_INST #undef __FOO_EXTERN 

So this is still quite a lot of letters, but at least you do not need to be careful to synchronize with different sets of template declarations, and you do not need to explicitly iterate over all possible combinations of types. In my case, I had a class template with two type parameters and several member function templates with an additional type parameter, and each of them could take one of 12 possible types. 36 lines is better than 12 3 = 1728, although I would prefer the preprocessor to sort through the type list for each parameter in some way, but I could not figure out how to do this.

As a remark, in my case I compiled a DLL where I need all the templates that need to be compiled, so in fact the instances / declarations of the templates were more like __FOO_EXTERN template __FOO_API ...

0
source share

All Articles