Skip to main content
GameDev.net gamedev.net
🔒 Locked

template class instantiation problem

Started by FxMazter Jun 24, 2004 at 4:16 PM 6 replies 1k views
Original Post
FxMazter
FxMazter
Ok, I figured it would be best to start a new thread, since the old one was a bit confusing... I'm having problem with template class Instantiation. The thing is that I have a class, whith a static variable which is initialized to the value of a static variable in a template class. Something like: template class TemplateClass { public: static SomeData* pData; }; template SomeData* TemplateClass::pData = new SomeData(); class ClassUsingTemplateClassData { public: static SomeData* pData; }; SomeData* ClassUsingTemplateClassData::pData = TemplateClass::pData; After some testing I have come to the conclusion that ClassUsingTemplateClassData::pData is beeing instatiated before the TemplateClass::pData. So when using the TemplateClass::pData we get a crap value ! :( Does anyone know how to solve this? Thank you! Look at this: Just copy this into a .cpp file and run it. You will see in which order the data is instantiated:

#include <windows.h>
#include <string>

//Some class with data
class SomeData {
};

//Function that makes new data for static variables
//sClassName is just for debuggin purpose...
SomeData* getNewData(const char* sClassName) {
	std::string message = "Instantiating ";
	message += sClassName;
	message += "::pData \n\n";
	message += "getData new SomeData() for: \n";
	message += sClassName;
	MessageBox(NULL, message.c_str(), "START", MB_OK);

	return new SomeData();
}

//This is just some template class containing SomeData*
//This data is then redistributed to a couple of other
//classes
template <class T>
class TemplateClass {
public:
	static SomeData* pData;
};
//Get new SomeData() for TemplateClass<T>
template <class T>
SomeData* TemplateClass<T>::pData = getNewData("TemplateClass<SomeClass>");

//Just a class for use as a parameter in TemplateClass<SomeClass>
class SomeClass {
};

//This function retrieves the pData from TemplateClass<SomeClass>
//But since normal classes are instantiated before template
//classes, if a normal class uses this function to set it's static
//pData... the TemplateClass<SomeClass>::pData won't exist at that
//time!
SomeData* getTemplateClassData(const char* sClassName) {
	std::string message = "Instantiating ";
	message += sClassName;
	message += "::pData \n\n";
	message += "getData TemplateClass<SomeClass>::pData for: \n";
	message += sClassName;
	MessageBox(NULL, message.c_str(), "START", MB_OK);

	return TemplateClass<SomeClass>::pData;
}

//This class uses the data from TemplateClass<SomeClass>
//But since ClassUsingTemplateClass::pData will be instantiated
//earlier than TemplateClass<SomeClass>::pData, this pointer
//will be crap :(
class ClassUsingTemplateClass {
public:
	static SomeData* pData;
};
SomeData* ClassUsingTemplateClass::pData = getTemplateClassData("ClassUsingTemplateClass");




int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

		MessageBox(NULL, "APPLICATION MAIN() START", "START", MB_OK);
		
		//This code will test if the ClassUsingTemplateClass::pData is valid
		if(ClassUsingTemplateClass::pData != 0)
			MessageBox(NULL, "Data is OK!", "START", MB_OK);

		MessageBox(NULL, "APPLICATION MAIN() END", "END", MB_OK);
		PostQuitMessage(0);
}


The code is simple, and I have commented the important stuff... Would appreciate any help on how to solve this :) Thank you!
FxMazter
FxMazter
Heh, as you might understand I'm no guru in this matter at all :)

I have now found out that it's not directly the instantiation
that is the problem, since I could do like this:

SomeData* getTemplateClassData() {
if(TemplateClass::pData == 0)
TemplateClass::pData = new SomeData();

return TemplateClass::pData;
}

SomeData* ClassUsingTemplateClassData::pData = getTemplateClassData();

Well... something similar at least...

This would mean that it's not the instantiation that is the
problem, but rather the initialization of the static pData
variable. And the intresting thing is that after
TemplateClass::pData = new SomeData() has
been executed in the getTemplateClassData(), it
no longer executes the initialization line at:

template
SomeData* TemplateClass::pData = new SomeData();

If someone would like to add something to enlighten me about
this I would appreciate :)
joanusdmentia
joanusdmentia
At a guess I'd say that the static of the template class isn't being instantiated until after everything else. If the class wasn't templated then this wouldn't happen as all statics in the same .CPP file should be instantiated in order, however templates are effectively generated code so I'm unsure as to what the standard says to do here. Forcing an instantiation of the specific specialisation you're after may fix this, but I'm not sure. Try the following for your ClassUsingTemplateClass:

class ClassUsingTemplateClass {public:	static SomeData* pData;};TemplateClass<SomeClass> instantiateMe_;SomeData* ClassUsingTemplateClass::pData = getTemplateClassData("ClassUsingTemplateClass");


If the fails give this main() function a go:
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {		MessageBox(NULL, "APPLICATION MAIN() START", "START", MB_OK);		ClassUsingTemplateClass::pData = TemplateClass<SomeClass>::pData;				//This code will test if the ClassUsingTemplateClass::pData is valid		if(ClassUsingTemplateClass::pData != 0)			MessageBox(NULL, "Data is OK!", "START", MB_OK);		MessageBox(NULL, "APPLICATION MAIN() END", "END", MB_OK);		PostQuitMessage(0);}


Also, if the two statics are defined in separate .CPP files then this approach is really really bad as order of instantiation is not guarentteed between different .CPP files.
"Voilà! In view, a humble vaudevillian veteran, cast vicariously as both victim and villain by the vicissitudes of Fate. This visage, no mere veneer of vanity, is a vestige of the vox populi, now vacant, vanished. However, this valorous visitation of a bygone vexation stands vivified, and has vowed to vanquish these venal and virulent vermin vanguarding vice and vouchsafing the violently vicious and voracious violation of volition. The nly verdict is vengeance; a vendett o
FxMazter
FxMazter
Thx for the help. Your first sollution should defenitely work,
but since I don't know for which instantiations of TemplateClass I will be using ...

The second sollution should also work, but the thing is that
this data needs to be initialized during the static data
initialization...

But since I have all the info of SomeData creation for each
class defined in that class as typedef's i did this in the
getTemplateClassData():

template
SomeData* getTemplateClassData(const char* sClassName) {
if(TemplateClass::pData != 0) {
//then use it
} else {
//manually initialize it here:
TemplateClass::pData = getNewData();
}

return TemplateClass::pData;
}

and then also skip the static initialization:


template
SomeData* TemplateClass::pData = (TemplateClass::pData != 0) ? TemplateClass::pData : getNewData();

Just so that it doesn't initialize it all over again...
Verg
Verg
How about something like:

SomeData** ClassUsingTemplateClassData::ppData = &(TemplateClass<SomeClass>::pData);


By the time everything is instantiated, the SomeData class object should exist... so you could get the reference through more indirection. Kind of hacky, though.
joanusdmentia
joanusdmentia
Hmm, sounds very hack-ish to me. I'm thinking a redesign may be in order, have you considered singletons?
"Voilà! In view, a humble vaudevillian veteran, cast vicariously as both victim and villain by the vicissitudes of Fate. This visage, no mere veneer of vanity, is a vestige of the vox populi, now vacant, vanished. However, this valorous visitation of a bygone vexation stands vivified, and has vowed to vanquish these venal and virulent vermin vanguarding vice and vouchsafing the violently vicious and voracious violation of volition. The nly verdict is vengeance; a vendett o
Shannon Barber
Shannon Barber
Every type T will receive it's own static pData member;
TemplateClass::pData is a different variable from TemplateClass::pData. To fix this you would create a common (non-template) base-class to contain the shared data.

struct BaseTemplateClass
{
static SomeData* ms_data;
};

//In source file
static SomeData BaseTemplateClass::ms_data* = new SomeData;

template
struct TemplateClass : BaseTemplateClass
{
};

Now the next problem is that the code that creates a pointer to ms_data would rely on the order of initialization of seperate translation units - which is undefined. Don't take a pointer to it, you don't need to. You can already access it whenever you want with TemplateClass::pData;

If you really have to have a pointer, then take a pointer to a pointer (called a handle), or a reference to a pointer.

class ClassUsingTemplateClassData {
public:
static SomeData** hData;
};
SomeData** ClassUsingTemplateClassData::hData = &TemplateClass::pData;


Then you can use it like this
(*hData)->member;
or
hData[0]->member;
The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.