模板中的静态成员变量,具有多个dll

时间:2008-12-29 16:38:31

标签: c++ dll visual-studio-2005 static templates

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

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

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

我看到一些使用“extern”的建议解决方案,但我认为我不能使用它,因为我的代码应该与visual studio 2002和2005一起使用。

谢谢。

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

7 个答案:

答案 0 :(得分:4)

还有以下解决方案:

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

详细说明如何做到这一点:

Anteru's blog Explicit template instantiation

答案 1 :(得分:3)

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

答案 2 :(得分:2)

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

答案 3 :(得分: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)

答案 4 :(得分:1)

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

首先,您使用另一个类(非模板)来保存此静态值 - 或者使其成为全局值? - 并从dll中导出。

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

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

然后你可以导出。

中包含的静态变量
__declspec(dllexport) int Foo<int>::StaticMember = 0;

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

虽然真正的问题是你为什么要这样做,但从技术上讲,DLL可以跨进程使用,只有一个副本存储在内存中。您是否真的希望所有流程只有一个静态版本,或者每个流程一个版本?

答案 5 :(得分: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

祝你好运。

答案 6 :(得分:0)

extern template实例化被接受到标准草案之前,Microsoft似乎实现了VC ++编译器的扩展。

如果使用非标准扩展,VC ++编译器将生成警告; VS.NET(2003)及以上版本引用了此warning描述以获取详细信息。此警告也会针对VS 6.0列出。

我个人从未尝试使用此扩展程序,所以我无法保证这个建议。显然我正在限制微软Visual Studio的这个答案(我看到你对Unix的评论),但我希望它可能有用。