Search code examples
c++templatesdllexplicit-instantiation

C++ member function explicit template instantiation across DLL


I am creating a DLL in C++ using Visual Studio 2013 on Windows 8.1 Update 1. There is a class called XMLData which has a public member function called getAttribute.

XMLData.h

namespace DDGL
{
    class DLL_EXPORTED XMLData
    {

       ...

       // const char* is used to avoid problems with trying to pass an
       // std::string over DLL boundaries
       template <typename Type> Type getAttribute (const char* attribute) const;

       ...

    };
}

Inside the DLL, every use is instantiated as you'd expect and works fine.

However, inside an application I will of course get undefined references for the <typename Type>'s that haven't been used within the DLL.

So, I try to use explicit template instantiation (I'd rather not put the implementation in header, for a learning exercise if anything):

XMLData.cpp

namespace DDGL
{

    ...

    // getAttribute definition

    ...

    template float XMLData::getAttribute(const char* attribute) const;

    ...

}

However, I still get an unresolved external in the application using the DLL:

Output

error LNK2019: unresolved external symbol "public: float __thiscall DDGL::XMLData::getAttribute<float>(char const *)const " (??$getAttribute@M@XMLData@DDGL@@QBEMPBD@Z) referenced in function "class std::shared_ptr<class DDGL::IGameObject> __cdecl SimpleExplodingRocketDeserialiser(class DDGL::XMLData)" (?SimpleExplodingRocketDeserialiser@@YA?AV?$shared_ptr@VIGameObject@DDGL@@@std@@VXMLData@DDGL@@@Z)

DLL_EXPORTED

#ifdef DDGL_DLL_BUILDING
    #define DLL_EXPORTED __declspec(dllexport)
#else
    #define DLL_EXPORTED __declspec(dllimport)
#endif

Where am I going wrong?


Solution

  • The problem was that, whilst I was indeed explicitly instantiating the template correctly, I was not exporting the symbol for each instantiation.

    The solution was to do the following:

    namespace DDGL
    {
        class DLL_EXPORTED XMLData
        {
    
           ...
    
           // const char* is used to avoid problems with trying to pass an
           // std::string over DLL boundaries
           template <typename Type> Type getAttribute (const char* attribute) const;
    
           ...
    
        };
    
        DLL_TEMPLATE_CLASS_MEMBER float XMLData::getAttribute(const char* attribute) const;
    }
    

    Where DLL_TEMPLATE_CLASS_MEMBER was defined as:

    #ifdef DDGL_DLL_BUILDING
        #define DLL_TEMPLATE_CLASS_MEMBER template DLL_EXPORTED
    #else
        #define DLL_TEMPLATE_CLASS_MEMBER extern template DLL_EXPORTED
    #endif
    

    Doing this correctly exported the symbols for the explicit template instantiation and allowed me to use them outside the DLL.