Singleton/release build weirdness (VC++ 6)

Started by
18 comments, last by benjamin bunny 20 years, 8 months ago
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]

____________________________________________________________www.elf-stone.com | Automated GL Extension Loading: GLee 5.00 for Win32 and Linux

Advertisement
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.
"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 only verdict is vengeance; a vendetta held as a votive, not in vain, for the value and veracity of such shall one day vindicate the vigilant and the virtuous. Verily, this vichyssoise of verbiage veers most verbose, so let me simply add that it's my very good honor to meet you and you may call me V.".....V
try making the Foo constructor private and see what it throws up.
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?
"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 only verdict is vengeance; a vendetta held as a votive, not in vain, for the value and veracity of such shall one day vindicate the vigilant and the virtuous. Verily, this vichyssoise of verbiage veers most verbose, so let me simply add that it's my very good honor to meet you and you may call me V.".....V
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.
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

____________________________________________________________www.elf-stone.com | Automated GL Extension Loading: GLee 5.00 for Win32 and Linux

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]
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 ;-)
Anything posted is personal opinion which does not in anyway reflect or represent my employer. Any code and opinion is expressed “as is” and used at your own risk – it does not constitute a legal relationship of any kind.
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.
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

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

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

This topic is closed to new replies.

Advertisement