模板,静态和DLL

问题描述 投票:2回答:2

我试图在定义中导出一个带有静态变量的函数模板。

.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)
}
c++ templates static visual-studio-2017 c++17
2个回答
0
投票

你需要使用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();
}

0
投票

一般情况下,你已经进入了痛苦的道路。您需要控制实例化过程。这可以做到,但我怀疑重新思考你尝试用Foo解决的实际问题可能更有益。您可以将GetFoo的实现保留在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();定义符号,因此链接器不会抱怨。

© www.soinside.com 2019 - 2024. All rights reserved.