views:

2876

answers:

7

My code is built to multiple .dll files, and I have a template class that has a static member variable.

I want the same instance of this static member variable to be available in all dlls, but it doesn't work: I see different instance (different value) in each of them.

When I don't use templates, there is no problem: initialize the static member in one of the source files, and use __declspec(dllexport) and __declspec(dllimport) directives on the class. But it doesn't work with templates. Is there any way to make it work?

I saw some proposed solutions that use "extern", but I think I can't use it because my code is supposed to work with visual studio 2002 and 2005.

Thank you.

Clarification: I want to have a different instance of static variable per each different type of template instantiation. But if I instantiate the template with the same type in 2 different dlls, I want to have the same variable in the both of them.

+2  A: 

Create template specialization and then export the static members of the specialization.

chappar
Thanks, it works. But this way I have to create a specialization for each type, and I lose the whole point of the templates. Is there any solution without it?
Igor Oks
see the link in the solution I mentioned to keep keep the template flexibility
balint.miklos
+1  A: 

There are two fixes for this problem which I can see.

First is that you use another class, one that is not a template, to hold this static value - or make it a global? - and export that out of the dll.

The other is slightly mor complicated in that you instantiate the template in the code and export that instantiated templated value(s). So to give an example say I had a special kind of linked list templated class and needed to have a static value shared across the DLL's. I wrote the code to be templated but it is only really used for some small number of types. I would instantiate the classes as such:

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

Then you could export the static variables contained within.

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

(Or something like that, I'm a bit rusty with doing dll export/import.)

Though the real question is why would you want to do this, as technically a DLL can be used across processes with only one copy stored in memory. Do you really want there to only be one version of the static for all processes, or one per process?

Daemin
you can explicitly instantiate only the static: template int SpecialFooList<int>::staticMember; within one cpp file then you don't need to specialize the whole template. what you do currently is not valid C++ and i'm not sure what it should mean :)
Johannes Schaub - litb
if you want to explicitly instantiate a whole template, you do it with template class SpecialFooList<int>;
Johannes Schaub - litb
Okay, fixed the (serious) coding errors, thanks for the heads up.
Daemin
Thank you. I tried to instantiate only the static, but it didn't solve the issue (the other dll doesn't find it at the linking time). Even after the fix, you are still instantiating the template and not the static - Are you sure that it works? Regarding your question: I use only one process
Igor Oks
i'm out of the game. i'v got no idea how things work in windows dlls :) good luck. i'll play a little with them next time tho.
Johannes Schaub - litb
litb: thank you! also, any idea, how to make this work with unix .so files?
Igor Oks
Igor: Just my opinion that I would avoid having static members inside dll's that are also accesible from the outside. So does this mean that there will only ever be one instance of your process running at a time?
Daemin
+2  A: 

The problem is that each different template instantiation is a different type with its own static variable that is not shared with other instances that have different template parameters. You could provide a non-template base class that contains the static variable.

Judge Maygarden
ahh, THATs what he meant! i was looking at his question not knowing what he means. i think you nailed it. nice
Johannes Schaub - litb
No, I want to have a different instance of static variable per each different type of template instantiation. But if I instantiate the template with the same type in 2 different dlls, I want to have the same variable in the both of them.
Igor Oks
+1  A: 

You already try this usage :

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

Note that variable need beam initialized.

More info : http://msdn.microsoft.com/en-us/library/ms997537.aspx http://www.flounder.com/hooks.htm

Good luck.

lsalamon
A: 

Prior to extern template instantiations being accepted into the draft standard it appears Microsoft implemented an extension for the VC++ compiler.

The VC++ compiler will generate a warning if the non-standard extension is used; VS.NET (2003) and above refer to this warning description for details. This warning is also listed against VS 6.0.

I personally have never attempted to use this extension so I'm unable to vouch for this suggestion. Obviously I'm restricting this answer to Microsoft Visual Studio (I saw a comment from you regarding Unix) but I post in the hope that it may prove useful.

Henk
+4  A: 

There exists the following solution, as well:

  • in the library: explicitly instantiate some template specialization and share them with dllexport
  • in the main program:
    • if the specialization is available it will be used from the library
    • if the specialization is not available it is compiled in the main program

The desciption in detail how you can do this:

Anteru's blog Explicit template instantiation

balint.miklos
A: 

There appears to be a way to do this with fewer limitations for the code that uses the template class.

Make the static member a pointer. Create a global map which has fixed known type and can be exported from the DLL. The map uses the typeid() of the class as key and the address of the "global variable per class" as value. Initialise the static member through a function that tests whether the class already exists in the map and if so forces the second version of the class (in the second DLL) to point to the static variable of the first version of the class.

In this way every DLL has a distinct static object, but every DLL also has a pointer and all the pointers point to the same one of the static objects.

Here's some pseudo-code, assuming the static's type is the same as the template parameter (but should be easily adapted for other cases).

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)
James