我有一个第三方dll,其中包含一个包含多个特化的模板类。我在Linux上有自己的专业,试图编译一个Windows DLL,但会导致链接器错误。
我尝试了一下,发现模板头上的dllimport规范可能是原因,删除它会解决我的问题。但是我不想修改或复制标题,因为它可能会随着第三方库的每次更新而中断。
这是重现我的问题的最小例子
test.h - dll / so header:
#ifdef _WIN32
#ifdef EXPORT
# define LIB_EXPORT __declspec(dllexport)
#else
# define LIB_EXPORT __declspec(dllimport)
#endif
#else
# define LIB_EXPORT
#endif
template <typename A> class LIB_EXPORT Foobar
{
public:
virtual void helloWorld(){}
virtual ~Foobar(){}
};
test.cpp - dll / so impl:
#define EXPORT
#include "test.h"
template class __declspec(dllexport) Foobar<int>;
main.cpp - 示例程序:
#include "test.h"
//explicit instanciation - does not help
template class __declspec(dllexport) Foobar<char>;
int main(int argc, char** argv)
{
Foobar<char> a;
a.helloWorld();
}
是否有一种干净的方法可以在我的可执行文件中完整地实例化Foobar?
使用的编译器:Visual Studio 2010,g ++ mingw w64 4.9.1
答案 0 :(得分:6)
我知道你说你不想修改标题,因为它可能会破坏第三方库更新,但是定义模板的标题没有正确设置。希望您可以让您的供应商修改其标题,以便更加友好地导入/导出。
目标:
在dll /中定义(导出)模板特化,然后使用(导入)该特化到您的exe。
我们不想要只导入或导出模板的每个专业化,因此我们从类中删除LIB_EXPORT。
template <typename A> class Foobar {
...
}
我们做但是要导入/导出模板的特定专业化。我们将转发声明特化,然后在您希望它驻留的编译单元中显式实例化它。
由于您还使用gcc构建,因此您需要使用'extern'关键字。 Visual Studio 2010不会为模板实现它。
#ifdef _WIN32
# define TEMPLATE_EXTERN
#ifdef EXPORT
# define LIB_EXPORT __declspec(dllexport)
#else
# define LIB_EXPORT __declspec(dllimport)
#endif
#else
# define TEMPLATE_EXTERN extern
# define LIB_EXPORT
#endif
最终的前瞻声明似乎是
TEMPLATE_EXTERN template class LIB_EXPORT Foobar<int>;
我们在这里明确地实例化了模板类,因为我们在头文件中的努力已经关闭了编译器的自动实例化功能。
#define EXPORT
#include "test.h"
template class Foobar<int>;
标头的默认状态是使用任何非int类型隐式实例化Foobar类。 int专门化在gcc上特别标记为'export',在win32上标记为__declspec(dllimport)。因此,您可以在任何地方进行其他专业化。
#include "test.h"
// explicit instantiation
template class Foobar<char>;
int main(int argc, char** argv)
{
Foobar<char> a;
a.helloWorld();
}