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!

Comments

Comments powered by Disqus