Jump to content
  • Advertisement
Sign in to follow this  
ironlungs

Singleton Question

This topic is 4131 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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!

Share this post


Link to post
Share on other sites
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 := 1

int offset = 1 - 1 := 0

ms_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]

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!