C++ tricks, #6: Explicit template instantiation
Today we take a look at explicit template instantiations (yet another post which is a direct result of user feedback :) ). With explicit template instantiations, you can define a template and instantiate it in a DLL, so clients don’t even have to see the implementation of the template. All you need is a language extension (extern template
) which will become part of C++0x, and is currently supported by GCC, Intel and MSVC.
The DLL
Let’s assume we have this great class:
template <typename T>
class Container
{
public:
Container (int size);
~Container ();
T Get (int index) const;
void Set (int index, T value);
private:
T* data_;
};
and we want to provide our clients an instance which works with integers only. For this to work, we put the definition into a separate file (you’ll see later why), container_impl.h
:
template <typename T>
Container<T>::Container (int size)
{
data_ = new T [size];
}
template <typename T>
Container<T>::~Container ()
{
delete [] data_;
}
template <typename T>
T Container<T>::Get (int index) const
{
return data_ [index];
}
template <typename T>
void Container<T>::Set (int index, T value)
{
data_ [index] = value;
}
Then we include both in a file in our DLL, let’s call it container_in.cpp
like this:
#include "container.h"
#include "container_impl.h"
template class __declspec(dllexport) Container<int>;
Now clients can already use the integer container, by including only container.h
which does not contain the definitions!
More flexibility
But what if clients want to use a float container? With the current solution, they’ll get an error that the definition is not available. To get it working, the client has to include container_impl.h
as well. But now, Container<int>
will be instantiated twice, which leads to an error. The workaround is to define the instantiation as extern template
, which means don’t generate code for this template even if the definition is available (for some reason or another, I just tried and it also works without the extern
with Visual C++ 9). The finished file is:
#ifdef DLL_EXPORT // Set to true when you build the dll
#define API __declspec(dllexport)
#else
#define API __declspec(dllimport)
#endif
template <typename T>
class Container
{
// Container declaration
};
#ifndef IN_ // Note: For VC++, you can leave out the extern
extern template class API Container<int>;
#endif
The container_in.cpp
file has to #define IN_
now. The client works like this:
#include "../shared_lib/templ.h"
#include "../shared_lib/templ.hpp"
#include <iostream>
int main (int, char**)
{
Container<float> c1 (5); // Will be instantiated here
c1.Set (3, 13.37f);
std::cout < c1.Get (3) < std::endl;
Container<int> c2 (4); // Will be linked in from the DLL
c2.Set (1, 4711);
std::cout < c2.Get (1) < std::endl;
}
That’s it!