• Advertisement
Sign in to follow this  

What I use where (some) other ppl use singletons.

This topic is 4437 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

In many cases, it is necessary to provide global instance of some class. In such case, singletons are usually used. IMO it is wrong because singleton is made to do two things: 1: define global instance accessible from elsewhere edit: that is (usually)created on first use 2: forbid creation of other instances second part is necessary only in device drivers (when two classes can not coexist) It is not wise to implement first _and_ second in any case when you in fact just need first alone. So, i use that simple thing if i want global object:
// edit:
// whole mess is necessary to solve construction order problems - this thing is
// constructed on first use so you can use it from other constructors (but not
// recursively).

#define DECLARE_GLOB_OBJECT(Type,Name) \   
  Type *Name();

#define DEFINE_GLOB_OBJECT(Type,Name) \   
  Type *Name(){ \   
    static Type instance; \   
    return &instance; \   
  };

// version that allocates on heap:
#define DEFINE_GLOB_OBJECT_HEAP(Type,Name) \     
  Type *Name(){ \   
    static std::auto_ptr instance(new Type); \     
    return instance; \   
  };

// when need something fancier, define it manually.








The idea is that implementation details* is hidden from client code - everything uses uniform GlobBlaBla() like things. So even if you have to use singletons (are coding something very low level so few instances can't coexist), it (IMO) could be good idea to create another DEFINE_GLOB_OBJECT_SINGLETON that uses singleton - this way implementation details about possibility of coexistence of few instances is hidden from code that uses the object. (*if few instances of class could coexist of not it is low level implementation details. Really low level, i.e. at level of working with hardware almost directly) [Edited by - Dmytry on December 30, 2005 6:22:52 AM]

Share this post


Link to post
Share on other sites
Advertisement
But isn't the second point an essential part of the definition of a singleton?
Otherwise it really is just a global class instance that suffers from the same deficiencies as a singleton (e.g. order of destruction).





Share this post


Link to post
Share on other sites
Wouldn't it be easier to just have the class instantiated once and then have a pointer to that class passed around?

Share this post


Link to post
Share on other sites
Quote:
Original post by darookie
But isn't the second point an essential part of the definition of a singleton?
Otherwise it really is just a global class instance that suffers from the same deficiencies as a singleton (e.g. order of destruction).

First: this is just what to do if you _need_ 1 but do not _need_ 2 . In such case, you need to provide global entry point, but don't need to limit to one instance. That's not where singleton should be used. But in reality many programmers use singletons in such cases, and practically implement 1 (what they need) AND 2 (what they do not need or even what is bad for their code).
The code I posted is equivalent to singleton but is
a.) simpler because it does not do 2
b.) it actually could be made to do 2 if ever needed without breaking the code that accesses the global.

For example, that's what you should be using for logging class. You could need several instances of logging class, but in beginning need just one.

Share this post


Link to post
Share on other sites
Quote:
Original post by ChaosX2
Wouldn't it be easier to just have the class instantiated once and then have a pointer to that class passed around?

it is theoretically good, but in really many cases you can not do that, and in such cases programmers usually use singletons even if they absolutely don't need "single" part.

Share this post


Link to post
Share on other sites
I don't understand. Your implementation is a Singleton. Also, how is it better (or different) than this implementation?
    class foo
{
public:
static foo * Instance()
{
static foo instance;
return &instance;
}
private:
foo();
~foo();
foo( foo const & );
foo operator =( foo const & );
};
Also, if you need define a global instance accessible from elsewhere but don't care it being unique, you could just do this:
   extern bar global_bar; 

Share this post


Link to post
Share on other sites
JohnBolton: i explained how it is diffferent. My instance absolutely is not Singleton
Maybe it should be called "ton" pattern, i don't know.
Example:
.h
DECLARE_GLOB_OBJECT(Logger,GlobPhysicsThreadLog)
DECLARE_GLOB_OBJECT(Logger,GlobRenderingThreadLog)


.cpp
DEFINE_GLOB_OBJECT(Logger,GlobPhysicsThreadLog)
DEFINE_GLOB_OBJECT(Logger,GlobRenderingThreadLog)

That's example of creating two instances of class Logger. Without modifying class itself. That are acessible in uniform manner. If Logger is singleton, it is not possible. But you may need several logs (esp. when working on multithreading app.)

Singletons have their use: when you are positively sure you can not and will never need to have several instances coexisting.

Share this post


Link to post
Share on other sites
Quote:
Original post by Dmytry
JohnBolton: i explained how it is diffferent.
Example:
.h
DECLARE_GLOB_OBJECT(Logger,GlobPhysicsThreadLog)
DECLARE_GLOB_OBJECT(Logger,GlobRenderingThreadLog)


...
DEFINE_GLOB_OBJECT(Logger,GlobPhysicsThreadLog)
DEFINE_GLOB_OBJECT(Logger,GlobRenderingThreadLog)

That's (almost) same as

extern Logger GlobPhysicsThreadLog;
extern Logger GlobRenderingThreadLog;

...

Logger GlobPhysicsThreadLog;
Logger GlobRenderingThreadLog;

The only difference is that with your approach the instances get intialised upon first use...

EDIT: Blast! John was faster [smile]

Share this post


Link to post
Share on other sites
Quote:
Original post by JohnBolton
Quote:
Original post by Dmytry
JohnBolton: i explained how it is diffferent.
Example:
.h
DECLARE_GLOB_OBJECT(Logger,GlobPhysicsThreadLog)
DECLARE_GLOB_OBJECT(Logger,GlobRenderingThreadLog)


...
DEFINE_GLOB_OBJECT(Logger,GlobPhysicsThreadLog)
DEFINE_GLOB_OBJECT(Logger,GlobRenderingThreadLog)


I see. I would do it this way, then:

.h
extern Logger GlobPhysicsThreadLog;
extern Logger GlobRenderingThreadLog;

.cpp
Logger GlobPhysicsThreadLog;
Logger GlobRenderingThreadLog;

three words: initialization order problems. The thing that singleton is intended to solve.
With local static, it is created when execution goes over it.
Of course it could still have finalization order problem but them is rarer to be so bad.
edit: blast, darookie was faster[grin]

Actually, i'm is using it for class where i register other classes during initialization, using something like
register_class<MyClassFactory> blabla("MyClass");
(done with macroses)
The register_class will call GlobClassRegistry()->AddClass(MyClassFactory,"MyClass")
so I can create classes by name (i'm doing data driven architecture)

Share this post


Link to post
Share on other sites
Quote:
Original post by darookie
...
The only difference is that with your approach the instances get intialised upon first use...

EDIT: Blast! John was faster [smile]


But I deleted my post because what you wrote pointed out what I was overlooking, so you can have full credit.

Share this post


Link to post
Share on other sites
I have no singletons in my code. By this I mean that no objects enforce a 'one instance only' rule. However, I have several objects which de facto exist in only one globally-accessible instance (aside from their instances in the unit tests for the various modules). Moreover, they are not accessed in a naked way, but instead through a function that allows me to change the underlying object at will in unit tests, even though this never happens in practice while the game is running.

The three objects made available this way are:

  • Allocator : a pool allocator system, which is mainly accessible through operator new.

  • ContextStack : a stack of contexts, each context contains a GarbageCollector (a garbage collector that can be used to flush from memory all the objects allocated during the context when it is popped), Graphics (set of renderable objects, their positions, and the camera position) and a Clock (measures time, in seconds, since the context was pushed).

  • Resources : a resource system that loads and unloads resources based upon requests from the system.

Share this post


Link to post
Share on other sites
Quote:
Original post by Dmytry
Quote:
Original post by ChaosX2
Wouldn't it be easier to just have the class instantiated once and then have a pointer to that class passed around?

it is theoretically good, but in really many cases you can not do that, and in such cases programmers usually use singletons even if they absolutely don't need "single" part.


I am a firm believer in KISS. If a singleton design pattern does not make sense and you can't pass a pointer to the object, then just define a global instance of the class and use that.

Share this post


Link to post
Share on other sites
Quote:
Original post by Dmytry
three words: initialization order problems. The thing that singleton is intended to solve.


no. IMPLEMENTATIONS of singletons try to solve that, but it's not a rule defined in the DEFINITION of a singleton. it's just a matter of fact that order of initialisation is always a problem. but the OP shows a solution to this wich works, and is NOT a singleton.

a singleton only forces a class to have at most only one instance. thats the ONLY thing a singleton defines and forces. the rest is a c++ thing. order of initialisation of global objects is an issue, singleton or not.

Share this post


Link to post
Share on other sites
Quote:
Original post by Dmytry
three words: initialization order problems. The thing that singleton is intended to solve.
With local static, it is created when execution goes over it.


I'm not sure I understand how this solves initialization order problems, specifically since the objects are created as execution hits them. That means that your objects are created invisibly depending on the first use of the functions your macro creates.

For example, let's take two modules 'Foo' and 'Bar' that are created using your macros. If Bar HAS to be instantiated before Foo (to illustrate the problem), how would you keep track of that? Someone may unknowingly call the Foo function before Bar, creating the Foo before Bar and causing havoc. The initialization order problem is still there, but your solution (imo) makes it harder to track down which objects are created when and where since it's dependant upon execution.

Singletons or not, I'm a BIG fan of very explicit Create-Destroy / Init-Deinit construction pairs. That way I know exactly which objects / systems are created in what order, and conversely I know exactly which order they are destroyed.

Also, destruction orders are VERY important, and should never be ignored if it matters. And the worst part is, they're sometimes the hardest ones to track down / solve.

Share this post


Link to post
Share on other sites
Quote:
Original post by Grafalgar
I'm not sure I understand how this solves initialization order problems, specifically since the objects are created as execution hits them. That means that your objects are created invisibly depending on the first use of the functions your macro creates.

For example, let's take two modules 'Foo' and 'Bar' that are created using your macros. If Bar HAS to be instantiated before Foo (to illustrate the problem), how would you keep track of that? Someone may unknowingly call the Foo function before Bar, creating the Foo before Bar and causing havoc. The initialization order problem is still there, but your solution (imo) makes it harder to track down which objects are created when and where since it's dependant upon execution.

Singletons or not, I'm a BIG fan of very explicit Create-Destroy / Init-Deinit construction pairs. That way I know exactly which objects / systems are created in what order, and conversely I know exactly which order they are destroyed.

Also, destruction orders are VERY important, and should never be ignored if it matters. And the worst part is, they're sometimes the hardest ones to track down / solve.


this is the magic of it. if Bar has to exist before Foo, it simply gets called ala GetBar() there in teh Foo code, where Bar has to exist. that way, it will always get initialised first, no matter what the user uses first.

and if Bar and Foo call eachother recursively, the compiler should detect and inform.

Share this post


Link to post
Share on other sites
Quote:
Original post by davepermen
this is the magic of it. if Bar has to exist before Foo, it simply gets called ala GetBar() there in teh Foo code, where Bar has to exist. that way, it will always get initialised first, no matter what the user uses first.

and if Bar and Foo call eachother recursively, the compiler should detect and inform.


Hmm - good point. I can see how that'll work out. I still am, in general, not comfortable with objects that get initialized at seemingly unknown times - even when it comes to singletons, I prefer to create/initialize them in a very specific spot and destroy/deinit at another spot - it helps keep things clean organized.

Share this post


Link to post
Share on other sites
Quote:
Original post by Grafalgar
... I prefer to create/initialize them in a very specific spot and destroy/deinit at another spot - it helps keep things clean organized.


In a complicated system, the proper order of initialization is not always easy to determine nor is it easy to maintain. How do you deal with statically allocated data?

In a system where everything is guaranteed to be initialized before it is used, you don't have to worry about the order of initialization, because it is automatic.

Share this post


Link to post
Share on other sites
Quote:
Original post by JohnBolton
In a complicated system, the proper order of initialization is not always easy to determine nor is it easy to maintain. How do you deal with statically allocated data?

In a system where everything is guaranteed to be initialized before it is used, you don't have to worry about the order of initialization, because it is automatic.


My statically allocated major system components don't get initialized during construction. They are initialized explicitly by the main 'overarching' system :P I don't believe in big components being initialized and whatnot as soon as they are created.

Thus, in said big and complicated system, keeping all major system components' init/deinit functions closely together really helps to maintain the code. Whenever something is 'automatic' warning flags come up for me :P

Share this post


Link to post
Share on other sites
The creation-upon-first-reference technique is more than just a way to solve order-of-initialization. Consider that a "global" object is more than just global in the lexical scope, it is also global in time. That is, a truly global object is accessible from anywhere at any time. An object that exists only within the lifespan of another object is not truly global. In effect, it is a member of the object that limits its lifespan.

The the creation-upon-first-reference technique is simply a way to make an object at least appear global in time, whether it really is or not.

The word "global" in the definition of a Singleton includes being global in time, so that is why the Singleton pattern includes creation-upon-first-reference. Otherwise, the access is not truly global.

Share this post


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

  • Advertisement