使用MinGW-w64从DLL导出带有体外定义的类模板



当涉及类模板时,我正面临将一个微不足道的Windows可执行文件正确链接到一个微不足道的DLL与MinGW-w64(基于MSYS2的GCC 11.3.0)。最小复制器如下:

库(library.cpp)的完整代码为
template <class T> class __attribute__((dllexport)) TestClass
{
public:
void member() { __builtin_printf("member from libraryn"); }
void other_member();
};
template class __attribute__((dllexport)) TestClass<int>;
template <class T> void __attribute__((dllexport)) TestClass<T>::other_member () {}
我用 编译它
g++ -std=c++11 library.cpp -o library.dll -shared -Wl,--out-implib,library.dll.a -Wl,--output-def,library.def
程序(program.cpp)的完整代码为
template <class T> class __attribute__((dllimport)) TestClass
{
public:
void member() { __builtin_printf("member from programn"); }
void other_member();
};
extern template class __attribute__((dllimport)) TestClass<int>;
int main (void)
{
TestClass<int> test;
test.member();
return 0;
}
我用 编译它
g++ -std=c++11 program.cpp library.dll.a -o program.exe

程序到DLL的链接在undefined reference to TestClass<int>::member()中失败。结果表明,链接失败可以通过两种方式解决:

  1. 程序中的extern模板语句被注释掉。然后,编译器使用本地版本的模板,程序从program"中打印"成员。
  2. TestClass<T>::other_member的定义被从中注释掉。然后程序正确链接到库中的TestClass<int>::member,并打印"member from library"。

我理解第一点,这里避免了外部模板,并进行了局部隐式实例化。当我用优化编译代码时,也会发生这种情况。

但是第二点让我困惑。为什么TestClass<T>::other_member的出体定义会破坏TestClass<T>::member的输出?

免责声明:我在调试别人的程序,所以设计选择不是我的。

模板类通常用在头文件中。它们仅仅是定义,只有在使用实际类型实例化后才成为实际代码。

所以在这方面,我不明白如何将它导出为DLL中的符号:

template <class T> class __attribute__((dllexport)) TestClass

这确实有意义,因为编译器可以生成代码,因为类型是已知的:

template class __attribute__((dllexport)) TestClass<int>;

在我看来,这样做更有意义:

library.hpp

#if !defined(DLL_EXPORT_LIBRARY)
# if defined(_WIN32) && defined(BUILD_LIBRARY_DLL)
#  define DLL_EXPORT_LIBRARY __declspec(dllexport)
# elif !defined(STATIC) && !defined(BUILD_LIBRARY_STATIC)
#  define DLL_EXPORT_LIBRARY __declspec(dllimport)
# else
#  define DLL_EXPORT_LIBRARY
# endif
#endif
template <class T> class TestClass
{
public:
void member();
void other_member();
};
template class DLL_EXPORT_LIBRARY TestClass<int>;
template <class T> void __attribute__((dllexport)) TestClass<T>::other_member () {}

library.cpp

#include <library.hpp>
template <class T> void TestClass<T>::member()
{
__builtin_printf("member from libraryn");
}

program.cpp

#include <library.hpp>
int main (void)
{
TestClass<int> test;
test.member();
return 0;
}

构建:

g++ -std=c++11 library.cpp -I. -DBUILD_LIBRARY_DLL -o library.dll -shared -Wl,--out-implib,library.dll.a -Wl,--output-def,library.def
g++ -std=c++11 program.cpp -I. library.dll.a -o program.exe

据我所知:
library.cpp中没有(显式)实例化类模板,或所述类的成员函数。

template class __attribute__((dllexport)) TestClass<int>;

将是一个(显式)模板特化前向声明…(因为它当然会)。
长话短说,如果(这是一个大胆的假设)我是对的,你只需要在TestClass<int>之后添加{}来拥有"定义"类型。

回答:

为什么TestClass::other_member的体外定义打破了TestClass::member的导出?

我更没信心了。但是为什么要定义TestClass专门化的成员函数呢?🧐它需要一个template吗?

cf:显式(完整)模板特化

最新更新