19

我的代码是针对多个 .dll 文件构建的,并且我有一个具有静态成员变量的模板类。

我希望这个静态成员变量的同一个实例在所有 dll 中都可用,但它不起作用:我在每个 dll 中看到不同的实例(不同的值)。

当我不使用模板时,没有问题:在其中一个源文件中初始化静态成员,并在类上使用 __declspec(dllexport) 和 __declspec(dllimport) 指令。但它不适用于模板。有什么办法让它工作吗?

我看到了一些使用“extern”的建议解决方案,但我认为我不能使用它,因为我的代码应该适用于 Visual Studio 2002 和 2005。

谢谢你。

澄清:我希望每个不同类型的模板实例都有一个不同的静态变量实例。但是,如果我在 2 个不同的 dll 中实例化具有相同类型的模板,我希望它们都具有相同的变量。

4

7 回答 7

4

也存在以下解决方案:

  • 中:显式实例化一些模板特化并与 dllexport 共享它们
  • 主程序中:
    • 如果专业可用,它将从库中使用
    • 如果专业化不可用,则在主程序中编译

详细说明如何执行此操作:

Anteru 的博客显式模板实例化

于 2009-06-30T18:05:35.553 回答
3

问题是每个不同的模板实例化都是不同的类型,具有自己的静态变量,不与具有不同模板参数的其他实例共享。您可以提供一个包含静态变量的非模板基类。

于 2008-12-29T17:21:24.597 回答
2

创建模板特化,然后导出特化的静态成员。

于 2008-12-29T17:12:16.737 回答
2

似乎有一种方法可以减少对使用模板类的代码的限制。

使静态成员成为指针。创建具有固定已知类型并可从 DLL 导出的全局映射。该映射使用类的 typeid() 作为键,使用“每个类的全局变量”的地址作为值。通过一个函数初始化静态成员,该函数测试该类是否已存在于映射中,如果存在,则强制该类的第二个版本(在第二个 DLL 中)指向该类的第一个版本的静态变量。

这样,每个 DLL 都有一个不同的静态对象,但每个 DLL 也有一个指针,并且所有指针都指向同一个静态对象。

这是一些伪代码,假设静态的类型与模板参数相同(但应该很容易适应其他情况)。

map<string,void*> dllexport the_map;  // instantiate this once in a single DLL

T *set_the_global(T *candidate) {
  map<string,void*>::iterator r = the_map.find(string(typeid(the_class<T>).name()));
  if(r == the_map.end()) {
    the_map[string(typeid(the_class<T>).name())] = (void*)candidate;
    return candidate;  // new class: use it as global storage location
  } else {
    return (T*)(r->second);  // class already has global storage location
  }
}

template <class T> class the_class {
  virtual void something();  // so RTTI exists
  static T *the_global;  // use this! always points to the same object
  static T one_per_dll;  // only used in initialisation
};
template<class T> the_class<T>::one_per_dll;
template<class T> the_class<T>::the_global = set_the_global(&the_class<T>::one_per_dll)
于 2009-12-18T15:20:06.390 回答
1

我可以看到这个问题有两个修复程序。

首先是你使用另一个类,一个不是模板的类来保存这个静态值——还是让它成为一个全局的?- 并将其从 dll 中导出。

另一个稍微复杂一些,因为您在代码中实例化模板并导出实例化的模板化值。举个例子,我有一种特殊的链表模板类,需要在 DLL 之间共享一个静态值。我编写了要模板化的代码,但它只真正用于少数类型。我会这样实例化这些类:

template <class T> class Foo;
template<> class Foo<int> {};

然后您可以导出其中包含的静态变量。

__declspec(dllexport) int Foo<int>::StaticMember = 0;

(或者类似的东西,我对 dll 导出/导入有点生疏了。)

尽管真正的问题是您为什么要这样做,因为从技术上讲,DLL 可以跨进程使用,而内存中只存储一个副本。您真的希望所有进程只有一个静态版本,还是每个进程一个?

于 2008-12-29T17:14:08.370 回答
1

您已经尝试过这种用法:

#pragma data_seg(".JOE")
HWND hWndServer = NULL;
HHOOK hook = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:.JOE,rws")  

请注意,变量需要光束初始化。

更多信息:http: //msdn.microsoft.com/en-us/library/ms997537.aspx http://www.flounder.com/hooks.htm

祝你好运。

于 2008-12-29T17:27:24.320 回答
0

extern template实例化被接受到标准草案中之前,微软似乎为 VC++ 编译器实现了一个扩展。

如果使用非标准扩展,VC++编译器会产生警告;VS.NET (2003) 及更高版本请参阅此警告说明以了解详细信息。此警告也针对VS 6.0列出。

我个人从未尝试使用此扩展程序,因此我无法保证此建议。显然,我将这个答案限制在 Microsoft Visual Studio(我看到了你关于 Unix 的评论),但我发帖希望它可能有用。

于 2008-12-29T19:52:37.240 回答