First of all, This is not a duplicate question, because 1) this is a linker problem, compiler is passed successfully because I have explicitly instantiated. 2) It's not about template class, but template member function, 3) I have some restrictions on the code structure so some existing tricks do not apply. I have searched my title here and the first few threads (40832391, 20330521, 25320619, 12848876, 36940394) are all about template class, not template member function. Some other threads are actually talking about failure of instantiation so actually a compiler issue but I have tried explicit instantiation and compiling has passed successfully, to repeat. So I hope you can suppress the temptation a little bit to close my question as duplicate and here comes my thread.
Environment:
- Windows 10 version 1803
- Visual Studio 2015 Update 3
- Debug x64 mode in VS
Source:
There are two projects:
1) DllProject, build as a dll, contains two sources: Dll.h and Dll.cpp.
Dll.h:
#pragma once
#ifdef _WINDLL
#define API_TYPE __declspec(dllexport)
#else
#define API_TYPE __declspec(dllimport)
#endif
class API_TYPE AClass {
public:
template <class T> void Func(T& data);
template <class T> void CallFunc(T& data) {
Func<T>(data);
}
};
Dll.cpp:
#include "Dll.h"
template <class T> void AClass::Func(T& data) {
data++;
}
template void AClass::Func<float>(float&); //attempt to explicitly instantiate
2) ExeProject, built as an exe, contains Exe.cpp.
Exe.cpp:
#include "Dll.h"
int main() {
AClass a;
float f = 0.f;
a.CallFunc<float>(f);
}
As you can see, what I want is to only call the template member function CallFunc
defined in dll which in turn calls another core template member function Func
that does the real work. I don't need to call Func
directly in exe so I don't need to export it in dll. Only CallFunc
is in the API that needs to be exported and it works fine. The dll project DllProject compiles correctly. The exe project ExeProject also compiles without any problem. But errors arise at linking time:
1>------ Build started: Project: ExeProject, Configuration: Debug x64 ------
1>Exe.obj : error LNK2019: unresolved external symbol "public: void __cdecl AClass::Func<float>(float &)" (??$Func@M@AClass@@QEAAXAEAM@Z) referenced in function "public: void __cdecl AClass::CallFunc<float>(float &)" (??$CallFunc@M@AClass@@QEAAXAEAM@Z)
1>C:\tmp\DllProject\x64\Debug\ExeProject.exe : fatal error LNK1120: 1 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
I have explicitly instantiated the template member function Func
in Dll.cpp at line 8, so compilation works. But linker fails to link to this instantiated function in dll from inside CallFunc
. I think the function is instantiated in dll, not in exe, because I specified __declspec(dllimport)
for the class. I don't know why CallFunc
can't find Func
; it's just right above it.
The sources are isolated and minimized from a big opensource (DllProject corresponds to the library itself while ExeProject to test code), so the implementation and declaration of template member function are separated into two files which is unavoidable in large project, and the code structure are not to be changed. The DllProject, as its name suggests, is to be build as a dll, though building and linking everything as static library works without any problem. I have done a lot of search, but threads in this forum and others either move the template implementation into the class declaration, or into the header by #include a .tpp file, all violating the above restrictions, or suggest explicit instantiation in various ways of expression, which I think I have done already because compiling is passed.
I have tried the following methods:
1) Compile under Release configuration
2) use inline
(and even __forceinline
)
3) Put explicit specialization in Dll.h in class:
class API_TYPE AClass {
public:
template <class T> void Func(T& data);
template<> void AClass::Func<float>(float&);
template <class T> void CallFunc(T& data) {
Func<T>(data);
}
};
4) Put explicit specialization in Dll.h outside class:
class API_TYPE AClass {
...
};
template<> void AClass::Func<float>(float&);
6) Put explicit instantiation in Dll.h outside class:
class API_TYPE AClass {
...
};
template void AClass::Func<float>(float&);
7) Add API_TYPE
in the declaration of Func
, template implementation and explicit instantiation
template <class T> void API_TYPE Func(T& data);
But none of them works and reports the same error.
8) Put explicit instantiation in Dll.h in class:
class API_TYPE AClass {
public:
template <class T> void Func(T& data);
template <class T> void CallFunc(T& data) {
Func<T>(data);
}
template void AClass::Func<float>(float&);
};
Compile error: error C2252: an explicit instantiation of a template can only occur at namespace scope
9) Put explicit specialization in Dll.cpp:
Compile error: error C2910: 'AClass::Func': cannot be explicitly specialized
I hope that's enough to demonstrate my effort. So, is there any chance to fix the "unresolved external symbol" under the aforementioned restriction? In case you forget it or didn't read it at all, the restriction is
The template implementation of
Func
must be separate from its declaration, i.e., must not be in class declaration or in header file.
In case you assume I don't know template function should be instantiated, to repeat, I have explicitly instantiated Func
, and tried it in many ways. So compiler is perfectly happy but the linker spews the error "unresolved external symbol." So why can't the linker find the already instantiated template member function? I also checked the output symbols in the import library DllProject.dll using dumpbin
. The symbol ??$Func@M@AClass@@QEAAXAEAM@Z
indeed resides in it, as factual as the earth rotating eastward. So do you know how to actively trace the behavior of the linker to figure out why it fails to find the function location instead of blind guessing? Thanks a lot.
template API_TYPE void AClass::Func<float>(float&);
– FormicCallFunc
is an inline function, so the executable will be the one instantiating and compilingCallFunc
and therefore callingFunc
, not the library. Thus it must be exported. – Fremitustemplate API_TYPE void AClass::Func<float>(float&);
works fine for me. – Formic