2008-12-29 85 views
15

我的代碼是爲多個.dll文件構建的,並且我有一個具有靜態成員變量的模板類。模板中的靜態成員變量,帶有多個DLL

我希望這個靜態成員變量的相同實例在所有dll中都可用,但它不起作用:我在它們中每個都看到不同的實例(不同的值)。

當我不使用模板時,沒有問題:在其中一個源文件中初始化靜態成員,並在該類上使用__declspec(dllexport)和__declspec(dllimport)指令。但它不適用於模板。有什麼辦法可以使它工作嗎?

我看到用「外部」一些提出的解決方案,但我想我不能使用它,因爲我的代碼應該是與Visual Studio 2002年和2005年

謝謝合作。

說明:我希望每個不同類型的模板實例化都有不同的靜態變量實例。但是,如果我在兩個不同的dll中實例化相同類型的模板,我想在它們中都有相同的變量。

回答

2

創建模板專門化,然後導出專業化的靜態成員。

+0

謝謝,它的工作原理。但是這樣我必須爲每種類型創建一個專業化,並且我失去了模板的全部要點。沒有它,有沒有解決方法? – 2008-12-29 18:54:03

+0

請參閱我提到的解決方案中的鏈接,以保持模板的靈活性 – 2009-06-30 18:07:17

1

這個問題有兩個解決方法,我可以看到。

首先是你使用另一個類,一個不是模板,來保存這個靜態值 - 或使其成爲一個全局? - 並從dll中導出。

另一個有點複雜,因爲您在代碼中實例化模板並導出實例化的模板化值。舉個例子,我有一個特殊的鏈接列表模板類,需要在DLL中共享一個靜態值。我編寫了代碼模板,但它只是用於少數幾種類型。我將實例化類如下:

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

然後,您可以導出其中包含的靜態變量。

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

(或者類似的東西,我有點生疏做DLL導出/導入。)

雖然真正的問題是,爲什麼你要做到這一點,在技術上一個DLL可以跨進程使用,只有一個副本存儲在內存中。你真的希望那裏只有所有進程的靜態版本,或者每個進程只有一個版本?

+0

您只能顯式實例化static:template int SpecialFooList :: staticMember;在一個cpp文件中,你不需要專門化整個模板。你目前所做的是不是有效的C++,我不知道它應該是什麼意思:) – 2008-12-29 17:24:21

3

問題是,每個不同的模板實例化都是一個不同的類型,其自身的靜態變量不與具有不同模板參數的其他實例共享。您可以提供一個包含靜態變量的非模板基類。

0

extern template實例化被接受進標準草案之前,它看起來像是Microsoft爲VC++編譯器實現了一個擴展。

如果使用非標準擴展名,VC++編譯器將產生警告;有關VS.NET(2003)及以上版本的詳細信息,請參閱此warning說明。此警告也是針對VS 6.0列出的。

我個人從未試圖使用此擴展名,因此我無法爲此建議擔保。很顯然,我限制了Microsoft Visual Studio的這個答案(我看到了你對Unix的評論),但是我希望它能證明有用。

4

存在着以下的解決方案,以及:

  • :明確實例一些模板專用,並與DLLEXPORT分享他們在主程序
    • 如果專業化可用,則將從圖書館使用
    • 如果特殊化重刑不存在,它是在主程序

編制詳細如何可以做到這一點,應將描述:

Anteru's blog Explicit template instantiation

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)