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,

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 *)(T*)1;
    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 <T*> (this);
  }

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.

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.

So in short, on a modern compiler, heed Jacob's words. On an older one, hacka hacka hacka until it works!

Scott Bilas