I've gotten a lot of feedback about the Singleton gem. Every single person wants to know why the hacky casting was necessary. One email I received from Jacob Marner stated it the best:
Hi Scott,
When I originally was experimenting with the recursive template singleton trick,
it was on an earlier version of Visual C++ (5 I think). It was a long time ago,
and I can't remember clearly, but I believe I tried using a simple cast and it
didn't work. After a lot of attempts where I changed things a bit then
recompiled to see what the disassembly looked like, I ended up with the hacky
cast solution. I wasn't concerned at all about portability, and like all things
I work on, once it works and I've tested it, I move on to the next task and
purge the old one from my cache.
I have just read your Gem in Game Programming Gems 1
about An Automatic Singleton Utility.
In the last program you present the following constructor
Singleton( void )
{
assert( !ms_Singleton );
int offset = (int)(T*)1 - (int)(Singleton
ms_Singleton = (T*)((int)this + offset);
}
Although this constructor works as it should even under
multiple inheritance, it is not portable (besides being quite
ugly :) ). It is not generally allowed to cast a pointer to
an integral and back again after manipulating it.
In it you assume that
1. int use the same amount of bits as a pointer. This is not
generally garanteed in C++.
2. That a no extra information is needed to represent pointers -
if for instance the memory model on the computer was segmented (like on
the 80286) the code would fail if the base class and the derived
class was in different segments.
I guess you don't know that a pointer for a class in C++ actually
also involves extra information about the pointer so it can easily
navigate the class. What this means is that when you do a typecast
for a class in C++ that involves multiple inheritance then the adress
might change! Because of this we can simply replace the constructor
with this simpler function that is portable and has the same functionality:
Singleton( void )
{
assert( !ms_Singleton );
ms_Singleton = static_cast
}
What is important here is to note that "this" and "ms_Singleton" will not
end up having the same system adress if Singleton is a base class but
not the first on derived.
So in short, on a modern compiler, heed Jacob's words. On an older one, hacka hacka hacka until it works!
Scott Bilas