First of all, I have found some seemingly related threads in this forum, but they do not help. For example, 33182246 is about static template member function, but the template member function in my question is not static, and the error therein is not the one I encountered. 12229396 is another irrelevant thread in which answerers are quarreling about whether it is allowed to export a static. But I don't intend to export a static in dll, and the question I am going to ask has nothing to do with it. As for 1053097 which is the first in my search result here, it is not about C++ at all. So I think my question is a new one and here comes the problem.
Environment:
- Windows 10 version 1803
- Visual Studio 2015 Update 3
- Debug x64 mode in VS
Source:
There are two projects in the solution:
1) DllProject, built as a dll, contains three sources: Dll.h, Dll2.h and Dll2.cpp.
Dll.h:
#pragma once
#include "Dll2.h"
#ifdef _WINDLL
#define API_TYPE __declspec(dllexport)
#else
#define API_TYPE __declspec(dllimport)
#endif
class API_TYPE AClass {
public:
template <class T> void CallFunc() {
BClass<T>::testStatic();
}
};
template void AClass::CallFunc<float>(); //explicit instantiation
Dll2.h:
#pragma once
template <typename T>
class BClass {
public:
static T m_Static;
static T testStatic();
};
template <typename T>
T BClass<T>::testStatic() {
return m_Static;
}
Dll2.cpp:
#include "Dll2.h"
template <typename T>
T BClass<T>::m_Static; //define the static
template class BClass<float>; //explicit instantiation
2) ExeProject, built as an exe, contains Exe.cpp.
Exe.cpp:
#include "Dll.h"
int main() {
AClass a;
a.CallFunc<float>();
}
The idea behind the solution structure is as follows. The exe program ExeProject
calls functions in dll, with Dll.h
specifying the interface. To abstract complexities as many large opensources do, Dll.h
only provides a wrapper of the underlying details, and it is this wrapper that is called by exe. The details are implemented by a template class BClass
in Dll2.h
and Dll2.cpp
. By design, BClass
is used only inside the dll so it is not qualified with __declspec(dllexport)
. Since BClass
is a template class, I explicitly instantiate it in Dll2.cpp
at line 6. Because CallFunc
is also a template, I explicitly instantiated it at line 27 of Dll.h. The problem is caused by the static member in BClass
. Since it is static, I need to define it and this is done in line 3-4 in Dll2.cpp
, so there is an m_Static
static variable inside dll that the dll uses through static function testStatic()
. The 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 LNK2001: unresolved external symbol "public: static float BClass<float>::m_Static" (?m_Static@?$BClass@M@@2MA)
1>C:\tmp\TestStatic\x64\Debug\ExeProject.exe : fatal error LNK1120: 1 unresolved externals
========== Build: 0 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========
As I said above, I have defined the static, I don't want the static to be exported, I have instantiated the template class BClass
and the template member function CallFunc
, I have specified API_TYPE
to AClass
so stuffs inside it should all be exported, but why still error?
The following are what I have tried. To make sure CallFunc is really exported from dll, I qualified it with API_TYPE
class API_TYPE AClass {
public:
template <class T> void API_TYPE CallFunc() {
BClass<T>::testStatic();
}
};
Then I received a compiling error: error C2491: 'AClass::CallFunc': definition of dllimport function not allowed. The same is true if I move the implementation out of the class.
However, everything works fine if I move the implementation of template function CallFunc
out of the header Dll.h
and into Dll.cpp
:
Dll.cpp:
#include "Dll.h"
#include "Dll2.h"
template <class T> void AClass::CallFunc() {
BClass<T>::testStatic();
}
and accordingly Dll.h
is changed to:
#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 CallFunc();
};
template void API_TYPE AClass::CallFunc<float>(); //explicit instantiation
where #include "Dll2.h"
is removed, only declaration of CallFunc
is in AClass
' declaration and API_TYPE
is added to the explicit instantiation of CallFunc
at the end (API_TYPE
is really erratic to use. Sometimes it works without it but sometimes not).
Now this is my question. For some reason, I would like to keep the implementation of the template member function calling static function in the header Dll.h (out of the class preferably), but as you can see, VS insists on putting the implementation in a .cpp file. So, is there any chance to work around this restriction and have the implementation code stay in header in VS? Thank you.