Singleton Question

Started by
8 comments, last by ironlungs 16 years, 10 months ago
Hi all. I was reading over this page: http://www.drizzle.com/~scottb/publish/gpgems1_singleton.htm but I'm a little bit confused as to what the purpose of the following two lines are in the constructor: int offset = (int)(T*)1 - (int)(Singleton <T>*)(T*)1; ms_Singleton = (T*)((int)this + offset); The explanation at the bottom of the page doesn't make sense to me. Can anyone explain this in a little more detail as to why this is needed? Thanks!
Advertisement
Please don't follow that reference. There has been much debate on these forums over whether the Singleton is even useful. That code looks horrible and nasty, but basically boils down to something like this (on most compilers, however the Standard C++ doesn't define the result):

(int)(T*)1 := 1(int)(Singleton<T>*)(T*)1 := 1int offset = 1 - 1 := 0ms_Singleton = (T*)((int)this + 0)ms_Singleton = this

On my current compiler (gcc) offset is indeed 0.

Kind of stupid and pointless, no? I have a feeling it may have something to do with a work around for compilers that don't implement the empty base class optimisation (maybe vc6 ?). [yes: the last paragraph of the text basically says the same]

A more appropriate example may be:

ms_Singleton = dynamic_cast<T*>(this);

Or, not using Singletons at all, my personal favourite choice. [grin]
Quote:int offset = (int)(T*)1 - (int)(Singleton <T>*)(T*)1;
ms_Singleton = (T*)((int)this + offset);


This bit of code contains wads and loads of implementation-defined and undefined behaviour. Namely:
  • Casting 1 to a pointer type (implementation-defined), two times.
  • Converting pointers back to integers (implementation-defined), three times.
  • Pointing to within a class' memory representation (undefined), one time.


This was done because, on some compilers and some machines, it allows to work around the flaws of an incorrect design in a way which feels more low-level and thus "faster", whatever that would mean for a one-shot initialization.

The problem here is that the singleton stores a pointer to its instance in its constructor Singleton(). However, the instance is of a derived type, T, and the conversion would thus require a cast from base to derived.

The only way to safely convert a base class pointer to a derived pointer in C++ is the use of dynamic_cast. For reasons that I cannot fathom (or rather, that I suspect to be rather foolish) the author attempts to perform that cast using, instead, a reinterpret_cast disguised as a C-style cast. Since it's well-known that using a reinterpret_cast to do this conversion is undefined behaviour, the author chose to counteract this problem using a solution which appears to work, but is still just as undefined.

I do not advocate using dyanmic_cast here, either. I would rather assign the newly created singleton (so, it would be of the correct type) as part of the GetInstance function. Or, even better, not use a singleton utility class at all, because it's not used often anyway.
Quote:Original post by rip-off
Kind of stupid and pointless, no? I have a feeling it may have something to do with a work around for compilers that don't implement the empty base class optimisation (maybe vc6 ?).


#include <iostream>class First { int x; public: virtual ~First() {}};class Second { virtual ~Second() {}};class Both : public First, public Second {};int main(){  int b = (int)(Both*)1;  int s = (int)(Second*)(Both*)1;  std::cout << s - b << std::endl;}


Outputs 8 on GCC. The idea here is that the pointer to Second points at the vptr for the Second part, which must have a fixed offset with regard to the members of Second in Both. Thus, Both has two vptr, one for the First subclass and one for the Second subclass.

Yes, but dynamic_cast<> figures it out right anyway...

I know that multiple inheritance is most commonly implemented with multiple vptrs in the object. Can't say I make use of multiple inheritance often though, it slipped my mind as a reason for the convoluted code in the example.
Quote:Original post by rip-off
I know that multiple inheritance is most commonly implemented with multiple vptrs in the object. Can't say I make use of multiple inheritance often though, it slipped my mind as a reason for the convoluted code in the example.


It's mentioned in the article as the reason for the convoluted code [smile]
The code:

int offset = (int)(T*)1 - (int)(Singleton <T>*)(T*)1;
ms_Singleton = (T*)((int)this + offset);

is basically a static cast. Use:

ms_Singleton = static_cast<T*>(this);

instead

Quote:Original post by mzeo77
ms_Singleton = static_cast<T*>(this);


It's the reverse (T* is a derived class of Singleton<T>), so static_cast would complain.

EDIT: never mind me.
Quote:Original post by ToohrVyk
Quote:Original post by mzeo77
ms_Singleton = static_cast<T*>(this);


It's the reverse (T* is a derived class of Singleton<T>), so static_cast would complain.


It is safe to use static_cast to downcast (cast to derived) provided you know that the type IS what you are casting to. The exception to this is with virtual base classes, in which case you can't use static cast.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Thanks for all the input guys. It makes a little more sense now. However, having looked over some suggestions I don't think I'll be making use of the example. That was close! Thanks!

This topic is closed to new replies.

Advertisement