'Exporting class template with out-of-body definitions from DLL with MinGW-w64
I am facing trouble properly linking a trivial Windows executable to a trivial DLL with MinGW-w64 (based on GCC 11.3.0 from MSYS2) when class templates are involved. A minimal reproducer is as follows.
The complete code of the library (library.cpp) is
template <class T> class __attribute__((dllexport)) TestClass
{
public:
void member() { __builtin_printf("member from library\n"); }
void other_member();
};
template class __attribute__((dllexport)) TestClass<int>;
template <class T> void __attribute__((dllexport)) TestClass<T>::other_member () {}
and I compile it using
g++ -std=c++11 library.cpp -o library.dll -shared -Wl,--out-implib,library.dll.a -Wl,--output-def,library.def
The complete code of the program (program.cpp) is
template <class T> class __attribute__((dllimport)) TestClass
{
public:
void member() { __builtin_printf("member from program\n"); }
void other_member();
};
extern template class __attribute__((dllimport)) TestClass<int>;
int main (void)
{
TestClass<int> test;
test.member();
return 0;
}
and I compile it using
g++ -std=c++11 program.cpp library.dll.a -o program.exe
The linking of the program to the DLL fails with undefined reference to TestClass<int>::member()
. It turns out that the linking failure can be solved in two ways:
- The extern template statement in the program is commented out. Then the compiler uses the local version of the template and the program prints "member from program".
- The definition of
TestClass<T>::other_member
is commented out from the library. Then the program properly links to theTestClass<int>::member
in the library and it prints "member from library".
I understand the first point, where the extern template is avoided and a local implicit instantiation takes place. This also happens when I compile the code with optimizations.
But the second point perplexes me. Why does the out-of-body definition of TestClass<T>::other_member
break export of TestClass<T>::member
?
Disclaimer: I am debugging someone else's program, so the design choices are not mine.
Solution 1:[1]
Template classes are usually used in header files. They are merely definitions, which only become actual code once instantiated with an actual type.
So in that respect I don't see how this can be exported as a symbol in a DLL:
template <class T> class __attribute__((dllexport)) TestClass
This does make sense, since the compiler can generate code because the type is known:
template class __attribute__((dllexport)) TestClass<int>;
Here is how it would make more sense in my opinion:
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 library\n");
}
program.cpp
#include <library.hpp>
int main (void)
{
TestClass<int> test;
test.member();
return 0;
}
build:
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
Solution 2:[2]
As far as I'm aware:
In library.cpp
there is no (explicit) instantiation of the class template, or the member function of said class.
template class __attribute__((dllexport)) TestClass<int>;
Would be an (explicit) 'template specialization forward declaration'... (Because of course it would).
Long story short, if (and that's a bold assumption) I'm right you just need to add {}
after TestClass<int>
to have the type "defined".
To answer:
Why does the out-of-body definition of TestClass::other_member break export of TestClass::member?
I'm even less confident. But why are you defining a member function of a specialization of TestClass
? ? Does it need a template
at all?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Brecht Sanders |
Solution 2 |