Templatized Unique Class Id Function Across Dlls

Started by
13 comments, last by emeyex 16 years, 3 months ago
In a paragraph, my dilemma is this: I have a templatized function which at runtime produces unique integer IDs per class; i.e., no matter how many times this function is called for class T, this function will return the same integer ID; however, because this function is templatized, it is instantiated in each dll it is called in. Because the function relies on a static variable inside the function, this means that if the function is called twice in two different dlls, then different IDs are returned, breaking the whole system. Code example:


// in a header file in A.dll
template<class t>
class ClassIdGenerator
{
public:
	static int GetClassId( )
	{
		static int gClassId = 0;
		if( gClassId == 0 )
		{
			// GetNextAvailableRuntimeClassId increments a global int and returns it
			gClassId = GetNextAvailableRuntimeClassId( );
		}
		return gClassId;
	}
};

// a type declared/defined in A.dll
struct T { };

// some code in a cpp file in A.dll
int TidInA = ClassIdGenerator<T>::GetClassId( ); // should return 1

// some code in a different cpp file in B.dll
int TidInB = ClassIdGenerator<T>::GetClassId( ); // should return 1, but will return 2


Without making this a debate on whether I should be using built-in rtti, does anyone see a way that I can make ClassIdGenerator<T>::GetClassId( ) return the same id in each dll? The function has to be declared in a header, as it's a template function; additionally, using __declspec(dllexport) doesn't work because the class is templatized. I'm at a bit of a loss...
Advertisement
Seems to me that the simplest solution would be to move the static int to your host dll, and have the template call a global function that exists in your host dll to retrieve and increment the id.
-----------------------------------Indium Studios, Inc.
Quote:Seems to me that the simplest solution would be to move the static int to your host dll, and have the template call a global function that exists in your host dll to retrieve and increment the id.


Unfortunately that doesn't quite work. I could move the static int gClassId to be a static member of the ClassIdGenerator class instead of a static variable within the function, but the effect is the same. Notice that the class is templatized; this means there is a different copy of the gClassId for each class that instantiates the ClassIdGenerator template. Hence I can't move the gClassId to be a totally global variable in some .cpp file. The other function GetNextAvailableRuntimeClassId( ) is a global function, but there's no issue with that.
Another (hopefully simpler) way to phrase this is:

Is there a dll-safe way of recognizing the first time a function is called for a given type, without using the built-in rtti. The canonical example is the following:
int gNextUniqueId = 0;template<class T>int Foo( ){  static int i = 0;  if( i == 0 ) // this is where we "recognize" the first call for type T   {    // increment gNextUniqueId    ++gNextUniqueId;    // store the current value of gNextUniqueId    // this essentially binds the current value to the type T for the    // duration of the program, because the next time this function    // is entered, i will not get re-assigned    i = gNextUniqueId;  }  return i;}


As mentioned before however, this function will get instantiated separately in each dll it is invoked from, producing multiple copies of
  static int i = 0; 

wrecking our ability to "recognize" the first time the function is called for type T.

Anyway, there's probably no way around this, but I hoped there might be some clever solution. Any help is appreciated!
You can export template implementations from a DLL; you just need to explicitly instantiate each used template and export each instantiation. Here is an article describing the syntax necessary.

However, using RTTI would probably work better.
Yeah, sorry, I should have mentioned that I was aware I could export explicit template instantiations... the point however is that there could be many many types which this function is used for, and having to remember to explicitly instantiate it for every new class I want to use this for might be a pain... I guess I'll leave the general template declared but undefined so at least I get a linker error or something if I forget to define the explicit version.

Anyway, thanks for the response, I'd been resigning myself to doing something along these lines, but your post confirms that's what I need to do.
Seriously, just use RTTI. It's already part of the language and won't give you these implementation difficulties that come about from creating your own ad hoc RTTI system like you are now.
Quote:Seriously, just use RTTI. It's already part of the language and won't give you these implementation difficulties that come about from creating your own ad hoc RTTI system like you are now.

I knew this would happen... :) It's all good though.

If the built-in RTTI could provide me with: type reflection, create object of type by 32-bit id, and consistency of type id across sessions and compilers, then yes, I would.

The part of my system that I was discussing in this thread was really the least important part of my RTTI system, but was useful for handling types that I didn't really care about that much. Now I'm resigned to ensuring that ALL types have consistent type ids across sessions and compilers, which ultimately is probably safer.

But thanks for the recommendation. I have used the built-in rtti quite a bit and am familiar with how it works, but ultimately find it fairly useless for my needs.
Just because C++'s RTTI doesn't do everything you want, doesn't mean you can't use it to build on.
#include <map>#include <typeinfo>class TypeInfoProxy {  public:    TypeInfoProxy(const std::type_info * t) : ti(t) {}    const std::type_info * ti;};inline bool operator<(const TypeInfoProxy & lhs, const TypeInfoProxy & rhs) {  return lhs.ti->before(*rhs.ti) != 0;}typedef std::map<TypeInfoProxy, int> TypeMap;TypeMap type_map;// export this function from a DLLint get_id(const std::type_info * ti) {  TypeMap::iterator itr = type_map.find(ti);  if (itr == type_map.end()) {    std::pair<TypeMap::iterator, bool> p = type_map.insert(std::make_pair(ti, GetNextAvailableRuntimeClassId()));    itr = p.first;  }  return itr->second;}template <typename T>int GetClassId(void) {  return get_id(&typeid(T));}

Only one function needs to be exported from a DLL, and no explicit template instantiations involved.
True, I could use that for the types I was discussing that DON'T require consistent IDs across sessions/compilers (which, admittedly, is the case I initially presented in this post). It just feels a shame to bring in the system's rtti just for this one purpose, and it feels so much more heavy weight. We'll see though, it looks like that would do the trick. I'll either go with that or implement serializable unique IDs for all types. Thanks.

This topic is closed to new replies.

Advertisement