Archived

This topic is now archived and is closed to further replies.

benjamin bunny

Singleton/release build weirdness (VC++ 6)

Recommended Posts

benjamin bunny    838
I'm using various singleton objects in my engine, all of which are based on the following template:
template <class T> 
class Singleton 
{
public:
	static T& inst()
	{
		static T instance;
		return instance;
	}
};
Classes derived from have private constructors, to ensure the class is really a singleton. They're declared like this
class Foo : public Singleton <Foo>
{
  friend Singleton<Foo>;
  Foo();
public:
  ...
}
This works fine, in debug mode, but when I turn on any optimisations in Visual C++ 6.0, my singleton classes' constructors are called multiple times. It turns out that subsequent calls to inst() after the first call the constructor, even though a static variable is used to instantiate the singleton object. The end result is there are 3 copies of the application, 2 copies of the texture manager, and my program crashes out spectacularly. On further investigation, it appears that although the constructors are called multiple times, the this pointer points to the same block of memory. The problem doesn't occur if I do the same thing without templates. Is this some kind of weird bug with Visual C++'s template support? Has anyone encountered it before? Is VC++ .net likely to fix the problem? ____________________________________________________________ www.elf-stone.com [edited by - benjamin bunny on August 7, 2003 8:08:22 AM]

Share this post


Link to post
Share on other sites
joanusdmentia    1060
What''s happening is that when optimisations are turned on, inst() is getting inlined, as expected. What isn''t expected, is that a different T instance is generated for every translation unit (.cpp) file that inst() appears in.

This is because the static keyword only applies to the translation unit. When inlining is disabled, the inst() function is only generated once as a normal (not inlined) function, and therefore there is only a single T instance. But when inlining is enabled, multiple inst() functions are generated for every time it''s called. In any one translation unit, there can end up being multiple inlined T instance declarations of which only 1 is kept since they''re static. The problem is that 1 is kept for every translation unit.

Now that you know what''s going wrong, try using this instead

template <class T>
class Singleton
{
static T instance;
public:
static T& inst()
{
return instance;
}
};

template <class T>
T Singleton<T>::instance;

Since it''s templated you can safely define the static member in the header, the compiler will ensure only 1 copy exists. I use this and haven''t ran into any problems with it.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
try making the Foo constructor private and see what it throws up.

Share this post


Link to post
Share on other sites
joanusdmentia    1060
quote:

try making the Foo constructor private and see what it throws up.


class Foo : public Singleton <Foo>
{
friend Singleton<Foo>; // <--- !!!

Foo();
public:
...
}

Happy birthday?

Share this post


Link to post
Share on other sites
civguy    308
By the way, the behaviour joanusdmentia described is only a bug in VC6. Well, it won''t comfort you much since you likely have no choice. I just wanted to let you know it''s not the standard defined behaviour.

Share this post


Link to post
Share on other sites
benjamin bunny    838
joanusdmentia: Thanks for the explanation. However your solution doesn''t work, because it doesn''t allow me to control the initialisation order of the singleton classes. (For example, with my code I can call App::inst() from my texture manager constructor and guarantee the application window is available before I try to load any textures. )

I guess the solution is to turn off optimisation for that block of code. Any idea how to do that with VC 6?

quote:
Original post by civguy
By the way, the behaviour joanusdmentia described is only a bug in VC6. Well, it won''t comfort you much since you likely have no choice. I just wanted to let you know it''s not the standard defined behaviour.


Yeah, I thought that behaviour was a bit unintuitive. Any idea if this is fixed in VC .net? I have a steadily growing list of complaints with the VC6 compiler, and I''ll ditch it as soon as I have a good excuse.

____________________________________________________________
www.elf-stone.com

Share this post


Link to post
Share on other sites
fizban75    130
How about if you try:


template<class T>
class Singleton
{
static T *instance;

public:
static T& inst()
{
if (!instance) {
instance = new T;
atexit(_destroy);
}
return *instance;
}
static void _destroy()
{
delete instance;
}
};

template<class T> T *Singleton<T>::instance = NULL;


edit: using atexit to schedule destruction

[edited by - fizban75 on August 7, 2003 10:16:35 AM]

Share this post


Link to post
Share on other sites
paulecoyote    1065
I''ve written an article on code project about this. Use the code however you want (as long as I can keep using it!)

Cheers,

Paul

http://www.codeproject.com/useritems/pdesingletontemplatenomfc.asp


P.S. If you like the article rate it nice and high ;-)

Share this post


Link to post
Share on other sites
eloso    122
quote:
I guess the solution is to turn off optimisation for that block of code. Any idea how to do that with VC 6?


In VC 6 you can use

#pragma auto_inline(off)

before and

#pragma auto_inline(on)

after your inst() function.

Share this post


Link to post
Share on other sites
Yanroy    122
Singletons and static variables defined in functions have both always confused me a lot, but it seems to me that instead of jumping through hoops to turn off your inlining, you could just define the body of the inst() function in the .cpp file? I''m sure with this many smart people, someone else has thought of it and figured out a flaw with it...

Also, just what purpose does static have when used on a local variable? It is my understanding that static makes variables one-per-translation-unit, but a local gets deallocated at return anyways, so isn''t it redundant?

--------------------


You are not a real programmer until you end all your sentences with semicolons; (c) 2000 ROAD Programming

You are unique. Just like everybody else.

"Mechanical engineers design weapons; civil engineers design targets."
"Sensitivity is adjustable, so you can set it to detect elephants and other small creatures." -- Product Description for a vibration sensor

Yanroy@usa.com

Share this post


Link to post
Share on other sites
benjamin bunny    838
quote:
Original post by fizban75
How about if you try:
..source...



I already tried this approach, but got exactly the same behaviour, strangely enough. Thanks anyway though.

eloso: Thanks for that, but it doesn't work, presumably because the function is in a template.

quote:
Original post by Yanroy
...but it seems to me that instead of jumping through hoops to turn off your inlining, you could just define the body of the inst() function in the .cpp file


Yes, that works, but I just like to be able to declare that a class is a Singleton and then use it as such. It seems neater that way.

However, since that isn't possible with VC6.0 I'm forced to abandon the Singleton template altogether. Here's my hacked solution:

//in singleton.h

#define SINGLETON_INSTFUNC(type) static type &inst(){static type inst; return inst;}

//Foo Singleton declaration

class Foo
{
Foo()
public:
SINGLETON_INSTFUNC(Foo)
}


The end result actually needs less typing than the initial solution, although it isn't exactly elegant.

[edited by - benjamin bunny on August 7, 2003 10:59:41 AM]

Share this post


Link to post
Share on other sites
benjamin bunny    838
quote:
Original post by Yanroy
Also, just what purpose does static have when used on a local variable? It is my understanding that static makes variables one-per-translation-unit, but a local gets deallocated at return anyways, so isn''t it redundant?


A static local variable isn''t deallocated at return. It''s essentially a global which can only be accessed by that function, which is initialised the first time the function is called.

Share this post


Link to post
Share on other sites
fizban75    130
quote:

I already tried this approach, but got exactly the same behaviour, strangely enough. Thanks anyway though.


Really? I have a singleton based on this approach that works fine in VC6, even in Release mode. When you tried it, the pointer was a static member of the class and not a local static variable in the function? I could understand you having the same problem in the second case, but not in the first...

Share this post


Link to post
Share on other sites
paulecoyote    1065
The original post''s problem is probably caused because the order and of creation and destruction of static variables is not guaranteed.

My solution (as I posted above) DOES use templates, has a singleton manager that ensures things are created and disposed in the correct order, and as a bonus is thread safe. Using the code from that link you can make any class a singleton with only 3 or 4 lines of changes in the original class.

There are cases of singleton creation using statics and the simple way of just returning a new instance to a variable if it''s null IF one thread is swapped out after it''s checked for null, another thread also checks for the null and is clear, then 2 singletons get created but then - of course - you loose track of one of them.

I''m not saying it''s a definitive way of doing things, but at least acknowledge that you took a look at the code! :-)

Share this post


Link to post
Share on other sites
eloso    122
quote:
eloso: Thanks for that, but it doesn''t work, presumably because the function is in a template.

Your are right. But the reason is not because the function is in a template but because vc6 is really buggy. Try compiling it with /Ob0 and you''ll see it''s not inlined any more. (like in debug mode)
I know, this is no solution because now none of your functions will be inlined any more, but it works

Share this post


Link to post
Share on other sites
benjamin bunny    838
quote:
Original post by fizban75
quote:

I already tried this approach, but got exactly the same behaviour, strangely enough. Thanks anyway though.


Really? I have a singleton based on this approach that works fine in VC6, even in Release mode. When you tried it, the pointer was a static member of the class and not a local static variable in the function? I could understand you having the same problem in the second case, but not in the first...


Yep. I even tried copying and pasting your code and got the same result.

quote:
The original post's problem is probably caused because the order and of creation and destruction of static variables is not guaranteed.

The order has nothing to do with it. A single static variable shouldn't be initialized more than once, regardless of the order.

[edited by - benjamin bunny on August 7, 2003 12:07:34 PM]

Share this post


Link to post
Share on other sites
benjamin bunny    838
Fizban: I just tried your code again, and it worked this time. For some reason VC didn''t build it properly last time. It seems to work now anyway. Problem solved.

Turns out that declaring the inst() method in the classes rather than using a template didn''t work in all cases anyway. The static variable was still contructed multiple times.

Share this post


Link to post
Share on other sites
fizban75    130
quote:

Fizban: I just tried your code again, and it worked this time. For some reason VC didn''t build it properly last time. It seems to work now anyway. Problem solved.


Ah, good to hear. I was a bit suspicious when you first said it didn''t work. ;-)

Share this post


Link to post
Share on other sites