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?
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.