-1

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.

user5280911
  • 723
  • 1
  • 8
  • 21
  • tl; dr... but at first glance in the definition of `BClass::testStatic()`, `return m_static;` should be `return BClass::m_static;`. Does it solve your issue? – YSC Jul 16 '18 at 11:20
  • My bad, I tend to forget MSVC is not up to the standard regarding template two-pass compilation... – YSC Jul 16 '18 at 11:24

1 Answers1

0

The workaround I found is move the definition of static variable from Dll2.cpp to Dll2.h. The implementation is still residing in Dll.h, nothing big is changed and everything is now working. For those who have doubt, Visual Studio handles this very well: the static has only one instance in the memory, as expected, and they are not exported, as desired. Pound.

Since I have solved my question, I had planned to just delete it, but I am surprised to see the downvote. Following is what I would like to say to this downvoter: I think I know what you are thinking: Sigh! This question is so low. The answer is all in the documentation or C++ Standard which I can recite immediately without even a single glance at it. I am an expert of C++, I can't tolerate such a low question ...

OK, I agree you are, but this question is all about problem-solving, without any intention to deeply discuss the theory of programming language so that I can write a phd dissertation. A practical problem does not have to be solved by showing off a profound mastery of the topic concerned. Just a small surface modification would suffice like what I did described at the beginning. So stop your arrogance and be open to all possibilities so that you can learn some problem-solving skills. It is not only good to you, but also good to the SO as a whole.

user5280911
  • 723
  • 1
  • 8
  • 21