Я создаю DLL
в C++
с помощью Visual Studio 2013
на Windows 8.1 Update 1
,
Есть класс под названием XMLData
которая имеет публичную функцию-член под названием 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;
...
};
}
Внутри DLL каждое использование создается, как и следовало ожидать, и работает нормально.
Однако внутри приложения я, конечно, получу неопределенные ссылки для <typename Type>
Это не были использованы в DLL.
Итак, я пытаюсь использовать явное создание экземпляра шаблона (я бы предпочел не помещать реализацию в заголовок для обучения в случае чего-либо):
XMLData.cpp
namespace DDGL
{
...
// getAttribute definition
...
template float XMLData::getAttribute(const char* attribute) const;
...
}
Тем не менее, я все еще получаю неразрешенный внешний вид в приложении, использующем DLL:
Выход
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
Куда я иду не так?
Проблема заключалась в том, что, хотя я действительно явно создавал экземпляр шаблона правильно, я не экспортировал символ для каждого экземпляра.
Решением было сделать следующее:
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;
}
куда DLL_TEMPLATE_CLASS_MEMBER
был определен как:
#ifdef DDGL_DLL_BUILDING
#define DLL_TEMPLATE_CLASS_MEMBER template DLL_EXPORTED
#else
#define DLL_TEMPLATE_CLASS_MEMBER extern template DLL_EXPORTED
#endif
Делая это правильно, экспортировал символы для явного создания шаблона и позволил мне использовать их за пределами DLL.
Хотя решение @OMGtechy работает, я думаю, что это излишнее.
Достаточно указать DLL_EXPORTED
как для объявления шаблона, так и для определения шаблона.
В заголовочном файле:
namespace DDGL
{
class DLL_EXPORTED XMLData
{
template <typename Type> DLL_EXPORTED Type getAttribute (const char* attribute) const;
};
}
В файле .cpp, определяющем функцию:
namespace DDGL
{
template<> DLL_EXPORTED float XMLData::getAttribute(const char* attribute) const {
// Function body goes here
}
}
Проверено на MSVC ++ 2017.
Несколько мыслей: думаю, было бы логично, если DLL_EXPORTED
по определению класса тоже работал над его шаблонными методами. Не шаблонные методы не требуют DLL_EXPORTED
снова на каждый метод. Так что же такого особенного в шаблонных методах? Я не знаю точно.