我试图在定义中导出一个带有静态变量的函数模板。
.DLL / foo.h中:
#ifdef _DLL
#define API __declspec(dllexport)
#else
#define API __declspec(dllimport)
#endif
class API Foo
{
public:
template<typename T>
static T& Get()
{
static T _instance;
return _instance;
}
static void Set();
}
我希望.dll和.exe所做的调用引用相同的“_instance”对象。我知道我可以通过在.cpp中定义静态变量来做到这一点。但在这种情况下,我正在处理模板,所以我有点卡住了。
编辑:发生了什么的例子..
.DLL / Foo.cpp中:
void Foo::Set()
{
Foo::Get<int>() = 10;
}
.EXE / main.cpp中:
int main()
{
auto & x = Foo::Get<int>();
x = 3;
std::cout << x; // 3
Foo::Set();
std::cout << x; // 3 (I want it to be 10)
}
你需要使用API
(__declspec(dllexport)
或__declspec(dllimport)
)在每个模板上单独标记,而不是在类代码中内联它。
Foo.h文件是:
#ifdef _DLL
#define API __declspec(dllexport)
#else
#define API __declspec(dllimport)
#endif
class API Foo
{
public:
template<typename T>
API static T& Get();
static void Set();
};
请注意,我们将Get()
标记与API
分开,尽管所有Foo
类也标有API
(类标记对模板功能没有任何影响,因此需要将它标记为单独)。而且这里没有Get
的实现 - 无论如何导出的函数都不能内联。
所以DLL代码(Foo.cpp)必须如下所示:
#include "foo.h"
template<typename T>
API T& Foo::Get()
{
__pragma(message("__imp_" __FUNCDNAME__)) // for debug
static T _instance;
return _instance;
}
void Foo::Set()
{
Foo::Get<int>() = 10;
}
请注意我们再次明确地使用API
(__declspec(dllexport)
)来实现函数体。这是至关重要的 - 编译器不会警告你,如果你在这里跳过API
,但没有这个 - Get
将不会被导出。
肯定在这一点上所有正确的 - 由__pragma(message("__imp_" __FUNCDNAME__))
生成的复制字符串(它看起来像__imp_??$Get@H@Foo@@SAAEAHXZ
)并在创建.lib文件中完全搜索(符号到符号)此字符串 - 在您构建dll之后。如果它存在 - 一切正常,否则没有意义继续(使用exe)
并在exe:
#include "../foo_dll/foo.h"
Foo::Get<int>() = 3;
Foo::Set();
if (Foo::Get<int>() != 10)
{
__debugbreak();
}
一般情况下,你已经进入了痛苦的道路。您需要控制实例化过程。这可以做到,但我怀疑重新思考你尝试用Foo
解决的实际问题可能更有益。您可以将Get
的Foo
的实现保留在Foo.h
中。
#ifdef _DLL
#define API __declspec(dllexport)
#else
#define API __declspec(dllimport)
#endif
template <class T>
class API Foo{
public:
static T &Get();
};
然后有一些其他的头,像Foo.impl
一样实现它
template <class T>
T &Foo<T>::Get() {
static T _instance;
return _instance;
};
在DLL中你可以在ConcreteClass.cpp
文件中实例化它,你可能做了你的整个Foo
:
#include "Foo.h"
#include "Foo.impl"
void dummy_instantiation() {
Foo<ConcreteClass>::Get();
}
如果你现在只在主程序中包含"Foo.h"
并将其与DLL的导出库链接,那么它将为Foo<ConcreteClass>::Get();
定义符号,因此链接器不会抱怨。