Singleton Discussion

A place for Ogre users to discuss non-Ogre subjects with friends from the community.
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Singleton Discussion

Post by nullsquared »

Singletons: don't use them. If you need global access, use a global variable. If you need a single instance, then only ever make 1 instance.
JohnJ wrote: However I actually agree with nullsquared that global access like this should be avoided if possible. He's absolutely right that use of singletons can be a very bad design, but I disagree that this is true in all cases 100% of the time. My approach to programming is simple: use features with care and common sense. So I acknowledge the fact that in very rare cases singletons (aka global access to variables or functions) is sometimes necessary, and even logical.
Elaborate.
User avatar
JohnJ
OGRE Expert User
OGRE Expert User
Posts: 975
Joined: Thu Aug 04, 2005 4:14 am
Location: Santa Clara, California
x 4

Re: Singleton Discussion

Post by JohnJ »

Singletons: don't use them. If you need global access, use a global variable. If you need a single instance, then only ever make 1 instance.
Singletons are by nature a syntactic synonym of global variables and functions under a namespace. So by saying "if you need global access, use a global variable", I don't think you realize that this is the same thing as a singleton.
Elaborate.
For one: Keyboard device input.

Show me one reason why a keyboard device class would be more cleanly implemented as a non-global access class. I'll keep an open mind.
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Re: Singleton Discussion

Post by nullsquared »

JohnJ wrote:
Singletons: don't use them. If you need global access, use a global variable. If you need a single instance, then only ever make 1 instance.
Singletons are by nature a syntactic synonym of global variables and functions under a namespace. So by saying "if you need global access, use a global variable", I don't think you realize that this is the same thing as a singleton.
No. A singleton, as its name implies, is a class that there should only ever be a single instance of. It has nothing to do with global access (which is what global variables are for, which themselves are also bad).
Elaborate.
For one: Keyboard device input.

Show me one reason why a keyboard device class would be more cleanly implemented as a non-global access class. I'll keep an open mind.
Here:

Code: Select all

    input::input input(root->hwnd());
    input.mouseSens = 0.25;
    input.resize(root->width(), root->height());

    /* ... */

    configure(*root, input, stateMgr, &opts);

    // game loop
    while(root->windowIsOpen())
    {
        /* ... */

        // capture input
        input();

        /* ... */
    }

    /* ... */
I have no clue at all why you would use singletons here (or anywhere for that matter).
User avatar
JohnJ
OGRE Expert User
OGRE Expert User
Posts: 975
Joined: Thu Aug 04, 2005 4:14 am
Location: Santa Clara, California
x 4

Re: Singleton Discussion

Post by JohnJ »

No. A singleton, as its name implies, is a class that there should only ever be a single instance of. It has nothing to do with global access (which is what global variables are for, which themselves are also bad).
If a singleton has nothing to do with global access, then getSingleton() or equivalent behavior shouldn't even exist - by your definition a pure singleton (without global access) would be nothing more than enforcement code to throw an error when more than once instance is created. That would be absolutely pointless for a number of reasons.

This is not how Ogre uses singletons, and this is not how I've ever seen singletons used (although I haven't worked with singletons a whole lot outside of Ogre).

Code: Select all

        input::input input(root->hwnd());
        input.mouseSens = 0.25;
        input.resize(root->width(), root->height());

        /* ... */

        configure(*root, input, stateMgr, &opts);

        // game loop
        while(root->windowIsOpen())
        {
            /* ... */

            // capture input
            input();

            /* ... */
        }

        /* ... */
First of all, that code is extremely hard to read. I couldn't have given a better illustration myself as proof of my earlier statement of it being better to capitalize class/namespace names:

Code: Select all

input::input input(root->hwnd());
...
input();
That has got to be the most ambiguous use of naming I've ever seen. I think you've just removed any chance of your example providing evidence for your method being clean and easy to read.

But to address whatever you're trying to prove with that code, what does input() do? Is it a functor? Is it a member function within some scope I don't see? What scope is this anyway? To quote from one of your earlier posts:
What happened to self-documenting code?
Last edited by JohnJ on Sat Jul 04, 2009 7:02 pm, edited 2 times in total.
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Re: Singleton Discussion

Post by nullsquared »

JohnJ wrote:
No. A singleton, as its name implies, is a class that there should only ever be a single instance of. It has nothing to do with global access (which is what global variables are for, which themselves are also bad).
If a singleton has nothing to do with global access, then getSingleton() or equivalent behavior shouldn't even exist - by your definition a pure singleton (without global access) would be nothing more than enforcement code to throw an error when more than once instance is created.
Precisely!
That would be absolutely pointless for a number of reasons.
And there you have it, singletons! ;)
This is not how Ogre uses singletons, and this is not how I've ever seen singletons used (although I haven't worked with singletons a whole lot outside of Ogre).
Just because Ogre uses them doesn't mean they're good.

Code: Select all

        input::input input(root->hwnd());
        input.mouseSens = 0.25;
        input.resize(root->width(), root->height());

        /* ... */

        configure(*root, input, stateMgr, &opts);

        // game loop
        while(root->windowIsOpen())
        {
            /* ... */

            // capture input
            input();

            /* ... */
        }

        /* ... */
First of all, that code is extremely hard to read. This is a good illustration to evidence my earlier mention of it being better to capitalize class/namespace names:

Code: Select all

input::input input(root->hwnd());
...
input();
That has got to be the most ambiguous use of naming I've ever seen. I think you've just removed any chance of your example providing evidence for your method being clean and easy to read.
Alright, I'll rename it to inputSystem. Same thing.
But to address whatever you're trying to prove with that code, what does input() do? What scope is this? How does the input data reach the code that controls whatever it controls?
What does input() do? I think the name is enough. It captures input. It reaches the destination code via the input object itself as well as buffered input callbacks.
User avatar
JohnJ
OGRE Expert User
OGRE Expert User
Posts: 975
Joined: Thu Aug 04, 2005 4:14 am
Location: Santa Clara, California
x 4

Re: Singleton Discussion

Post by JohnJ »

What does input() do? I think the name is enough. It captures input. It reaches the destination code via the input object itself as well as buffered input callbacks.
Sorry about the edit again, I made it clearer:

"But to address whatever you're trying to prove with that code, what does input() do? Is it a functor? Is it a member function within some scope I don't see? What scope is this anyway? To quote from one of your earlier posts: 'What happened to self-documenting code?'"
And there you have it, singletons! ;)
I agree that the singleton concept you defined is completely pointless and a bad idea in pretty much every case I can think of. But I don't think your definition of singleton = Ogre's/my definition of singleton.
Just because Ogre uses them doesn't mean they're good.
I didn't say that the fact that Ogre uses them makes it ok (in fact I would prefer if Ogre used singletons a lot less). I said that Ogre's use of singletons seems to be different from your definition - in other words the singleton classes could be theoretically rewritten as namespaces containing global variables and functions.
What does input() do? I think the name is enough. It captures input. It reaches the destination code via the input object itself as well as buffered input callbacks.
Where does it capture it to? Also saying it reaches the destination code "via the input object itself" is very hazy. Once I can decipher your code I'll show you a "static class" implementation (I won't use the word "singleton" to avoid ambiguity) and how much easier it is to read (IMO anyway).
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Re: Singleton Discussion

Post by nullsquared »

JohnJ wrote: "But to address whatever you're trying to prove with that code, what does input() do? Is it a functor? Is it a member function within some scope I don't see? What scope is this anyway? To quote from one of your earlier posts: 'What happened to self-documenting code?'"
It doesn't matter whether it's a functor or a member function. That's an encapsulation detail. What is important is the fact that it does input.
What does input() do? I think the name is enough. It captures input. It reaches the destination code via the input object itself as well as buffered input callbacks.
Where does it capture it to?
It doesn't matter. Once again, encapsulation.
Also saying it reaches the destination code "via the input object itself" is very hazy.
Notice the configure() function. Whatever needs input will get a reference to the input object.
Once I can decipher your code I'll show you a "static class" implementation (I won't use the word "singleton" to avoid ambiguity) and how it helps.
Why? It won't help at all, the current code works great as it is. Why bother with a "static class?"
User avatar
JohnJ
OGRE Expert User
OGRE Expert User
Posts: 975
Joined: Thu Aug 04, 2005 4:14 am
Location: Santa Clara, California
x 4

Re: Singleton Discussion

Post by JohnJ »

It doesn't matter whether it's a functor or a member function. That's an encapsulation detail. What is important is the fact that it does input.
Your view of encapsulation is wrong. Knowing that it "does" input isn't enough, "do" can include capturing, processing, applying, or halting program execution with a prompt. You function name should be "captureInput();" if this is what it does.
Notice the configure() function. Whatever needs input will get a reference to the input object.
Again, extremely vague. I hope you don't think encapsulation means you should be vague, because this is very bad practice. "configure()" can mean many different things. At a glance, I have no idea what that configure call is configuring. I'm not saying your code is unstructured, just that it's far from self-documenting.
Why? It won't help at all, the current code works great as it is. Why bother with a "static class?"
When I first began programming I wrote spaghetti code that "worked great as is", but that doesn't make it a good design.
User avatar
_tommo_
Gnoll
Posts: 677
Joined: Tue Sep 19, 2006 6:09 pm
x 5
Contact:

Re: Singleton Discussion

Post by _tommo_ »

I think that the whole discussion boils down to one thing:

OOP is not always enough.

So, there are problems where the "OOP laws" are actually an obstacle, and have to be ignored, so i don't agree completely with nullsquared. For example all the cases when you do know you need one and only one instance of something, etc (memory allocators, default log, filesystem managers)
Obviously, one must be careful in deciding when to ignore the OOP guidelines, as you could have a problem later :roll:
Like when you do realize that one and only one instance was not enough :D

Anyway i try to avoid as much as possible to use singletons, if carefully designed you don't have to overload constructors with parameter :)
Ideally, one object should be able to retrieve all its needed informations from its direct creator... when that isn't the case a Singleton could be in order.
OverMindGames Blog
IndieVault.it: Il nuovo portale italiano su Game Dev & Indie Games
User avatar
xavier
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 9481
Joined: Fri Feb 18, 2005 2:03 am
Location: Dublin, CA, US
x 22

Re: Singleton Discussion

Post by xavier »

I think too much attention is paid to the name (which is an entirely different discussion, one regarding the elevation of "design patterns" to first-class status).

Regardless, this particular focus on "object purity" always comes up at a certain point in one's evolution as a software engineer. At some point you get over the rigidity of this "doctrine", usually when you realize that blind adherence to this particular "principle" actually gets in the way of productivity.

You use whatever construct you need to do the job. No more, no less. If a "singleton" fits the needs, you use that construct. If you don't need a "singleton", you don't. Having any sort of immutable law that ever precludes doing things different, is an obstacle in itself.

Selah.
Do you need help? What have you tried?

Image

Angels can fly because they take themselves lightly.
User avatar
JohnJ
OGRE Expert User
OGRE Expert User
Posts: 975
Joined: Thu Aug 04, 2005 4:14 am
Location: Santa Clara, California
x 4

Re: Singleton Discussion

Post by JohnJ »

Exactly.

This is the same thing I tried to explain in the other thread but you explained it much better than I did:
I wrote:My approach to programming is simple: use features with care and common sense. So I acknowledge the fact that in very rare cases singletons (aka global access to variables or functions) is sometimes necessary, and even logical.
Also, something I almost forgot:
nullsquared wrote:It was already done. There are no arguments for using singletons, they can all be countered, and in the end, singletons are not to be used, period.
I would like to suggest to nullsquared, with respect, that statements like this be used with extreme caution. I don't know how old you are, but you should be aware that the more you know, the more you know there's even more that you don't know; statements like these risk being interpreted as either arrogance or a naive overconfidence typically associated with teenagers - I'm not assuming you are one so forgive me if this sounds like an insult; I'm merely suggesting that statements like these be phrased carefully to do yourself justice. I'm certainly not blameless myself in this regard, and have on more than one occasion made the same mistake, so I'm not blaming you.

(From an entirely abstract logical point of view, that statement is inherently non-factual. It's based largely on an opinion or personal experience that was incorrectly promoted to an overly broad factual assertion)
User avatar
JohnJ
OGRE Expert User
OGRE Expert User
Posts: 975
Joined: Thu Aug 04, 2005 4:14 am
Location: Santa Clara, California
x 4

Re: Singleton Discussion

Post by JohnJ »

Why bother with a "static class?"
First, it's not a "bother" - in most cases it's easier to do a "static class". The problem in fact is usually to be careful not to be lazy and make too many things static/global when they shouldn't be, not the other way around.

In the case of the keyboard example, I would ask, "why not?" Just hypothetically, if both methods were equally easy to use, which would make more sense when dealing with a purely singular object like a keyboard? Something to think about, anyway.

Here's another issue that singletons can help solve: Generally classes should be designed to be as self-contained as possible. This means to avoid references to "parent" classes as well as lateral class references whenever possible. Sometimes you can get into situations where accessing an inherently singular data object is necessary from multiple levels of your code, and storing 100 local references to this object across your code or multiple parent dereferences to resolve to the desired object becomes tedious and error prone. In this case, often the cleanest and most elegant solution is a global/static/singleton implementation of some sort.

Another reason to use them is it's just more convenient and productive in some cases. Again, singletons should be used with caution, but there are definitely cases where it makes programming easier without sacrificing functionality or clean design.
Last edited by JohnJ on Sat Jul 04, 2009 9:44 pm, edited 1 time in total.
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179
Contact:

Re: Singleton Discussion

Post by jacmoe »

I prefer a singleton over a plain old global.
And a singleton helps with ownership issues, often seen in global classes. (Like a logmanager). Because of it singularity.
Static utility classes are great, but not really comparable to singletons. They are like carrots and bananas.
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
CABAListic
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 2903
Joined: Thu Jan 18, 2007 2:48 pm
x 58
Contact:

Re: Singleton Discussion

Post by CABAListic »

@nullsquared: Since you always plead for injection over singletons, I'd like to ask you (and I'm serious, I'm not trying to tease you or anything) how you would solve the following situation? Suppose that Ogre were not using singletons. Apparently though there would still be a ResourceGroupManager which you'll probably have dealings with in your user code. Now, how do you access it? After all, the ResourceGroupManager is not created by your own code, so you do have to request it from Ogre at some point. Which means that Ogre needs to expose the pointer. Which means that, without singletons, you need a function in Ogre::Root. Which means that Ogre::Root's interface is going to change whenever a new manager is added to Ogre. Which may result in additional recompilations of your code segments.
Granted, this may not be too much of an issue because any such change to Ogre may likely trigger a recompile of your project, anyway. But still I'd be interested how (if at all) you'd solve that in principle.

Also, I would like to say that I really think a logsystem benefits from being implemented as a (self-constructing) singleton. That way you can simply start using it in your code without ever having to worry about where to construct it; it just works when needed, at least basic console output.
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Re: Singleton Discussion

Post by nullsquared »

CABAListic wrote:@nullsquared: Since you always plead for injection over singletons, I'd like to ask you (and I'm serious, I'm not trying to tease you or anything) how you would solve the following situation? Suppose that Ogre were not using singletons. Apparently though there would still be a ResourceGroupManager which you'll probably have dealings with in your user code. Now, how do you access it? After all, the ResourceGroupManager is not created by your own code, so you do have to request it from Ogre at some point. Which means that Ogre needs to expose the pointer. Which means that, without singletons, you need a function in Ogre::Root. Which means that Ogre::Root's interface is going to change whenever a new manager is added to Ogre. Which may result in additional recompilations of your code segments.
Granted, this may not be too much of an issue because any such change to Ogre may likely trigger a recompile of your project, anyway. But still I'd be interested how (if at all) you'd solve that in principle.
First of all, get rid of ResourceGroupManager. Ogre handles resources in these "resource groups" awfully in my opinion. And I'm sure others will agree. After all, that's what the whole refactoring things is about in 1.8/2.0.

Second, until then you use:
Root::getTextureManager()
Root::getMeshManager()
(interfaces would be needed for the other managers
Root::addResourceLocation
Also, I would like to say that I really think a logsystem benefits from being implemented as a (self-constructing) singleton. That way you can simply start using it in your code without ever having to worry about where to construct it; it just works when needed, at least basic console output.
What is wrong with this?

Code: Select all

std::clog << "logging stuff, yay!";
And if you really need your own class for it:

Code: Select all

class logger { ... };

// .hpp
extern logger log;

// .cpp
logger log;

// elsewhere
log << "logging stuff, yay!";
JohnJ wrote: In the case of the keyboard example, I would ask, "why not?" Just hypothetically, if both methods were equally easy to use, which would make more sense when dealing with a purely singular object like a keyboard? Something to think about, anyway.
So, basically, you agree that my method is perfectly fine, you're just too stubborn to also agree that your method is not necessary and not preferred?

Tell me, what happens when you want to simulate the input (not perfect example, but an example none-the-less)? In my case, simple.

Code: Select all

//input::input input(...);
// use simulated input, instead
input::simulatedInput input(...); // tada.  everything Just Works
Here's another issue that singletons can help solve: Generally classes should be designed to be as self-contained as possible. This means to avoid references to "parent" classes as well as lateral class references whenever possible. Sometimes you can get into situations where accessing an inherently singular data object is necessary from multiple levels of your code, and storing 100 local references to this object across your code or multiple parent dereferences to resolve to the desired object becomes tedious and error prone. In this case, often the cleanest and most elegant solution is a global/static/singleton implementation of some sort.
Data flow goes down, not up. Why would you need references to parent classes? (other than an object hierarchy, like a GUI, of course)

100 local references? No:

Code: Select all

class body
{
    private:
        world &_world; // body needs a reference to the world it's in for whatever reason

    public:
        body(world &w): _world(w) { ... }
};
1 reference. Not 100.
Another reason to use them is it's just more convenient and productive in some cases. Again, singletons should be used with caution, but there are definitely cases where it makes programming easier without sacrificing functionality or clean design.
You haven't actually provided a concrete example where singletons > not singletons. Just words and theories that are incorrect.
I would like to suggest to nullsquared, with respect, that statements like this be used with extreme caution. I don't know how old you are, but you should be aware that the more you know, the more you know there's even more that you don't know; statements like these risk being interpreted as either arrogance or a naive overconfidence typically associated with teenagers - I'm not assuming you are one so forgive me if this sounds like an insult; I'm merely suggesting that statements like these be phrased carefully to do yourself justice. I'm certainly not blameless myself in this regard, and have on more than one occasion made the same mistake, so I'm not blaming you.
I'm almost 16. And I make statements like that because I can back them up. You've still failed to provide an actual, concrete example where singletons > not singletons.
Your view of encapsulation is wrong. Knowing that it "does" input isn't enough, "do" can include capturing, processing, applying, or halting program execution with a prompt. You function name should be "captureInput();" if this is what it does.
Input is cause, stuff happening is effect. Processing, applying, or halting program execution with a prompt are all effect. Input does not care about effect, as it is the cause. The only thing it does (and, well, the only thing it can do) is capture input.
Again, extremely vague. I hope you don't think encapsulation means you should be vague, because this is very bad practice. "configure()" can mean many different things. At a glance, I have no idea what that configure call is configuring. I'm not saying your code is unstructured, just that it's far from self-documenting.
Wikipedia wrote: Encapsulation is the hiding of information in order to ensure that data structures and operators are used as intended and to make the usage model more obvious to the developer.
Of course you're not gonna know what configure() is, there is no context to my snippet.
mirlix
Goblin
Posts: 225
Joined: Mon May 01, 2006 12:03 am
Location: Germany
x 5

Re: Singleton Discussion

Post by mirlix »

My point of view is that singletons should only be used when you want to enforce that only one instance of this object should exists. Singletons shouldnt be used as global variables or to avoid passing pointers, this can actual be pretty harmful to your code. Singletons are fine if there is a concrete reason that only one instance of an class should exists. This can be the case for networking or data ports when accessing a connected computer,gadget or anything else would produce errors. If this unusual case crosses your way, then you should use singletons, to be sure there is only one instance. It wouldnt be enough to just make sure that you dont create two instance, you have to make it impossible, because otherwise debugging might be hell. Also try to make something that shouldnt be done impossible rather then advice in comments that it shouldnt be done. It will help you and your fellow programmes a lot :D

Just my two cents
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Re: Singleton Discussion

Post by nullsquared »

mirlix wrote:My point of view is that singletons should only be used when you want to enforce that only one instance of this object should exists. Singletons shouldnt be used as global variables or to avoid passing pointers, this can actual be pretty harmful to your code.
Excellent point.

Code: Select all

class willErrorIfMoreThanOne
{
    public:

        willErrorIfMoreThanOne()
        {
            static bool alreadyExists = false;
            assert(!alreadyExists && "make ONLY ONE of this class");
            alreadyExists = true;
        }
};
mirlix
Goblin
Posts: 225
Joined: Mon May 01, 2006 12:03 am
Location: Germany
x 5

Re: Singleton Discussion

Post by mirlix »

Code: Select all

class willErrorIfMoreThanOne
{
    public:

        willErrorIfMoreThanOne()
        {
            static bool alreadyExists = false;
            assert(!alreadyExists && "make ONLY ONE of this class");
            alreadyExists = true;
        }
};
This enforces that only one instance exists, but also limits you. Because if there is already one instance of this class there is no possibility to get access to this instance. Best would be if the constructor would return the same instance which already exists or if there isnt one create a new one. Sadly this isnt possible so the static getInstance() function is the best way I know to enforce that there is only one instance and at the same time make it possible to use the class everywhere in your code. This also makes this instance global, but for me this is rather a side effect which cant be avoid.
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Re: Singleton Discussion

Post by nullsquared »

mirlix wrote: Because if there is already one instance of this class there is no possibility to get access to this instance.
Why not? Whoever makes it will give it to others to use.

Code: Select all

void foo(thereShouldOnlyBeOne &bar)
{
    bar.doImportantStuff();
}

int main()
{
    thereShouldOnlyBeOne instance;

    foo(instance);
}
mirlix
Goblin
Posts: 225
Joined: Mon May 01, 2006 12:03 am
Location: Germany
x 5

Re: Singleton Discussion

Post by mirlix »

The problem I see with this approach is that it only shifts the possibility of an error. Now you cant get an error because two instances exist, but you cant get an error when you try to create a second instance. And when more then one people work on the project the possibility if very high that someone somewhere will try to create a second instance and it might even happen that this person creates the second instance before you create your first instance. Now your code reports an error because someone else did something wrong in a totally different place. This can get really ugly.

As a fan of defensive programming I would say, dont shift the error, prevent it. In this case one possibility to prevent the error is to make the constructor private and have a getInstance() method. Of course there are others way to achieve this, but I dont know any which works equally well then getInstance(). But I would be delighted to hear some other ideas to solve this problem.
CABAListic
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 2903
Joined: Thu Jan 18, 2007 2:48 pm
x 58
Contact:

Re: Singleton Discussion

Post by CABAListic »

nullsquared wrote: First of all, get rid of ResourceGroupManager. Ogre handles resources in these "resource groups" awfully in my opinion. And I'm sure others will agree. After all, that's what the whole refactoring things is about in 1.8/2.0.

Second, until then you use:
Root::getTextureManager()
Root::getMeshManager()
(interfaces would be needed for the other managers
Root::addResourceLocation
Apparently RGM was just one example of many, but I assume from this that you wouldn't address the issue. Fair enough.
What is wrong with this?

Code: Select all

std::clog << "logging stuff, yay!";
It is unformatted. It has no automatic timestamp, for instance. Granted you might not need that for a console output, but I would imagine you normally do have a higher level logsystem which routes formatted output to multiple targets (console, file). My system initialises console logging upon first use of the logging system for early failures and adds file logging later on.

Code: Select all

And if you [b]really[/b] need your own class for it:
[code]
class logger { ... };

// .hpp
extern logger log;

// .cpp
logger log;

// elsewhere
log << "logging stuff, yay!";
This is fine until you require another global which might want to log some stuff during construction. Since the order of creation is undefined for global variables, it's bound to fail. I agree that this should be avoided, but the Meyers Singleton is in fact equivalent to such a global, just without the order of initialisation problem, so in that case it has the edge over a simple variable.
User avatar
_tommo_
Gnoll
Posts: 677
Joined: Tue Sep 19, 2006 6:09 pm
x 5
Contact:

Re: Singleton Discussion

Post by _tommo_ »

nullsquared wrote: Why not? Whoever makes it will give it to others to use.
This has no point, because, if one makes it and is the only one that gives it to others, what you made has no point.
You could just check for existency in the "request creation" method as the instance is not public...
and this is what get methods and factories are for.
OverMindGames Blog
IndieVault.it: Il nuovo portale italiano su Game Dev & Indie Games
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Re: Singleton Discussion

Post by nullsquared »

mirlix wrote:Now you cant get an error because two instances exist, but you cant get an error when you try to create a second instance
What? There is only one instance. Whatever needs to access that instance will receive a reference to it. There is no room for error, because the instance will be only created in one spot.
CABAListic wrote: This is fine until you require another global which might want to log some stuff during construction.
I smell design issues. Not only is it a global, but it is a global with a non-trivial constructor that needs to log something?
_tommo_ wrote: This has no point, because, if one makes it and is the only one that gives it to others, what you made has no point.
What? You will simply use dependency injection. If something needs it, it will get a reference to it.
User avatar
JohnJ
OGRE Expert User
OGRE Expert User
Posts: 975
Joined: Thu Aug 04, 2005 4:14 am
Location: Santa Clara, California
x 4

Re: Singleton Discussion

Post by JohnJ »

And if you really need your own class for it:

Code: Select all

class logger { ... };

// .hpp
extern logger log;

// .cpp
logger log;

// elsewhere
log << "logging stuff, yay!";
That's almost exactly how my singleton implementation works, but mine is a little more flexible with construction. After seeing this, I'm starting to think the main disagreement between you/me on this is simply due to an inconsistency of the definition of "singleton".
Singletons shouldnt be used as global variables or to avoid passing pointers, this can actual be pretty harmful to your code. Singletons are fine if there is a concrete reason that only one instance of an class should exists. This can be the case for networking or data ports when accessing a connected computer,gadget or anything else would produce errors. If this unusual case crosses your way, then you should use singletons, to be sure there is only one instance.
Apparently we have an inconsistency of naming here. To me, "singleton" = "global class". To you, "singleton" = "debug assertion" (basically). I think the latter is a bad idea because a debug assertion does nothing to resolve the root problem, just defers it to another area of code.
As a fan of defensive programming I would say, dont shift the error, prevent it.
Exactly :)
So, basically, you agree that my method is perfectly fine, you're just too stubborn to also agree that your method is not necessary and not preferred?
If anything, I'm usually one of the first to mention that singletons are most definitely not necessary and in most cases not preferred. But I also accept the fact that there are (rare) cases where to singletons are in fact preferred. Although you've tried, you can't deny this. You can only try argue that every programmer in the world is wrong who makes the choice to use a singleton over whatever alternates there are, and this, needless to say, is going to be a losing battle.

As to whether your code is "perfectly fine" or not, I can't really tell because I don't know enough about it to say reliably, but from what I've seen it's pretty ambiguous and difficult to read in general, so no, I never said I agree that your method is perfectly fine.
Input is cause, stuff happening is effect. Processing, applying, or halting program execution with a prompt are all effect. Input does not care about effect, as it is the cause. The only thing it does (and, well, the only thing it can do) is capture input.
My point was "input" is a vague verb, and is therefore ambiguous in many cases. You're little code example proves this very nicely:

Code: Select all

input::input input();
input();
Not exactly self documenting.
Data flow goes down, not up. Why would you need references to parent classes? (other than an object hierarchy, like a GUI, of course)
I'm referring to object hierarchy, as in a game object storing a number of pointers to manager objects above it.
100 local references? No:

Code: Select all

class body
{
    private:
        world &_world; // body needs a reference to the world it's in for whatever reason

    public:
        body(world &w): _world(w) { ... }
};
1 reference. Not 100.
In the case of the code you posted, it looks like it's more likely to be problematic with chains of backward dereferences, like "body.world.root.input.keyboard.isKeyDown()" (just an example). Rather than "Keyboard->isKeyDown()". I'll assume for now that I don't need to explain why the former is horrible design, but let me know if you need me to explain it. Now I'm not saying you have a problem like this, but that it's my best guess as to where a singleton could help, considering I don't know much about the rest of your code.
And I make statements like that because I can back them up. You've still failed to provide an actual, concrete example where singletons > not singletons.
It's not about me, I don't have to prove anything to you. I think you don't understand the implication of your statement that "singletons are not to be used, period". You can't possibly back this up; it's logically an impossibility. To spell it out for you: you're basically saying that every programmer in the history of mankind who has ever used a singleton over the alternative was making the wrong decision. Good luck "backing that up".

Once that is understood, I'll point out that there are plenty of concrete examples where singletons > not singletons, all you have to do is open your eyes to them. You've even posted some yourself. "std::clog", for example, is just as much a singleton as "Ogre::LogManager::getSingleton()" for all practical purposes.
You haven't actually provided a concrete example where singletons > not singletons. Just words and theories that are incorrect.
To be perfectly honest I don't like arguing for singletons, because I don't like them in most cases anyway. I already gave you the example of the keyboard class. I could type up a long code example demonstrating exactly why it's advantageous, but at this point I've wasted enough time already on this thread.

But I really need to learn to stay out of pointless flamewars like these in the first place. I should have known better.
User avatar
steven
Gnoll
Posts: 657
Joined: Mon Feb 28, 2005 1:53 pm
Location: Australia - Canberra (ex - Switzerland - Geneva)
Contact:

Re: Singleton Discussion

Post by steven »

I don't see why I would need more than one EngineManger, GraphicsManager, LogManager, NetworkManager, InputManager and some others.

So why should I bother to pass references or pointers everywhere and not simply access them via singleton?
It makes lots of constructors and methods simpler.
Locked