One-liner for explicit instantiation of fixed set of types

Viewed 101

I have a fixed set of four types A, B, C and D, and a large amount of class and function templates that work with these types.

To reduce compile times, I would like to put the definition of these templates into .cpp files and explicitly instantiate the templates for this fixed set of types. However, the explicit instantiation introduces a lot of boilerplate code, which I would like to reduce. Is there an elegant way of explicitly instantiating function and class templates for a fixed set of types?

Here's some code to illustrate the problem:

#include <iostream>

class A { public: int foo() const { return 0; } };
class B { public: int foo() const { return 1; } };
class C { public: int foo() const { return 2; } };
class D { public: int foo() const { return 3; } };


template<typename T>
class SomeClass {
    /* could have a member variable of type A-D */
    T m_t;
    
    /* or several functions which take such a type */
    void printFoo(const T& t){
        std::cout << t.foo() << "\n";
    }
};


/* normal explicit instantiation */
//template class SomeClass<A>;
//template class SomeClass<B>;
//template class SomeClass<C>;
//template class SomeClass<D>;


/* or something with macros, but hopefully better than this: */

#define INSTANTIATE_FOR_ALL_TYPES \
INSTANTIATE_WITH(A) \
INSTANTIATE_WITH(B) \
INSTANTIATE_WITH(C) \
INSTANTIATE_WITH(D)

/* if this here could be one line instead of three, then you found the answer */
#define INSTANTIATE_WITH(TYPE) template class SomeClass<TYPE>;
INSTANTIATE_FOR_ALL_TYPES
#undef INSTANTIATE_WITH


int main(){
    return 0;
}

I wouldn't use explicit instantiation if it weren't certain from the design of the program that the types won't change. Also, I'm aware that for compiling the code once, the compile time is not affected by explicit instantiation. However, when writing tests which include many templates, and recompilation is done often, the effect is very noticeable. If there's another option to achieve shorter compilation times, I'm open to it.

1 Answers

For now, until someone finds a better solution, the following works well enough:

#define INSTANTIATE_FOR_ALL_TYPES(TYPE) template class SomeClass<TYPE>;
#include "instantiator.hpp"

with the contents of instantiator.hpp being

/* lack of a header guard is intentional */
INSTANTIATE_FOR_ALL_TYPES(A)
INSTANTIATE_FOR_ALL_TYPES(B)
INSTANTIATE_FOR_ALL_TYPES(C)
INSTANTIATE_FOR_ALL_TYPES(D)

#undef INSTANTIATE_FOR_ALL_TYPES

Typos and similar mistakes are caught by the preprocessor. This works well for instantiating several functions/classes with minimal code duplication:

#define INSTANTIATE_FOR_ALL_TYPES(TYPE) \
template class SomeClass<TYPE>; \
template class AnotherClass<TYPE>; \
template void foo( const TYPE& );
#include "instantiator.hpp"

If someone can spot problems with this approach, I'd welcome the feedback. But I'd prefer "this could cause problem x under circumstances y" over "macros are evil" and "this is not how you should use #include".

Related