• Advertisement
Sign in to follow this  

Local Singleton?

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

There are many threads here that ask about singletons and why not to use them, since there seem to be many cases where you should only have one instance of a class and it should have global access. Usually the answer is that a class almost never needs global access and if you only want one instance, don't create more than one. I'm completely sold on the first argument but it seems to me that it would be nice to enforce the second one. For example, the class might be a part of a library and it's users might create more than one instance of it (because they won't be convinced that they shouldn't). So I thought that something like a "Local Singleton" design pattern might be useful (and not as dangerous as the "regular" singleton). So I decided to write a small class that does this:

#include <cassert>

template <class T>
class LocalSingleton {
private:
	LocalSingleton(const LocalSingleton &);
	LocalSingleton & operator=(const LocalSingleton &);

	static int s_count;

public:
	LocalSingleton() {
	    if (++s_count > 1)
	    	assert(!"Can't create more than one instance");
	}
	~LocalSingleton() {
	    --s_count;
	    assert(s_count == 0);    // Just in case...
	}
};

template <class T>
int LocalSingleton<T>::s_count = 0;


Here's an example of it:

#include "LocalSingleton.h"

class Renderer : public LocalSingleton<Renderer> {};

class TextureManager : public LocalSingleton<TextureManager> {};


int main()
{
    Renderer r;              // OK
    // Renderer r2;          // Triggers an assertion
    TextureManager tm;       // OK
    // TextureManager tm2;   // Triggers an assertion
}


Of course, a compile-time error would have been better (instead of the assert() which will trigger at run-time), but this is just a simple example. So what do you think about this? Does it work properly? Is it stupid? Useless? The invention of the century? [grin]

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by Gage64
I'm completely sold on the first argument but it seems to me that it would be nice to enforce the second one.

Oh?

Take this code:

for (int i = 0; i < 10; ++i){
// Whatever
}

Do you usually accidentally create more than one loop iteration variable? Do you need the code to *enforce* that you must only ever create one variable in the for loop?

How about cout?
Do you ever accidentally create another std::ostream when you intended to use std::cout?

It's really not that hard to just not create an instance of a class if you don't want to. When was the last time you accidentally created a new instance when you intended to reuse an existing?

Quote:

For example, the class might be a part of a library and it's users might create more than one instance of it (because they won't be convinced that they shouldn't).

If they're not convinced that they shouldn't, then it can't be such a big disaster if they do it.
If it's one of the few cases where it doesn't ever make sense to create more than one instance, shouldn't the library user be able to understand it as well?
In most cases, it's not absolutely critical that only one object exists. And if it isn't absolutely critical, why enforce it? (You might think it makes no sense to have two renderers. Then someone tries to implement an application that uses two GPU's to view two separate windows, or whatever. So why enforce that there must be only one instance? Just because you can't imagine the uses for multiple instances, doesn't mean it should be impossible. There might be people more creative than you [wink])

Share this post


Link to post
Share on other sites
Quote:

Original post by Spoonbender
If it's one of the few cases where it doesn't ever make sense to create more than one instance, shouldn't the library user be able to understand it as well?


"Should" is the keyword here.[smile]

Quote:

if it isn't absolutely critical, why enforce it?


Why not?
Consider the example of a texture manager. One of it's purposes is to make sure that a texture isn't loaded more than once, and it does that by maintaining a list of all the loaded textures. If you create 2 texture managers and load the same texture with each one then the texture will be loaded twice and this wastes memory. Granted, this isn't exactly a disaster, but why not prevent it if you can? There might also be better examples than this one.

Quote:

Just because you can't imagine the uses for multiple instances, doesn't mean it should be impossible. There might be people more creative than you.


I'm sure there are, I'm just saying that this can be useful in certain situations. Also, if you'll want to allow for multiple instances later on, it is very easy to do (just remove the inheritance).

Share this post


Link to post
Share on other sites
Quote:
Original post by Spoonbender
You might think it makes no sense to have two renderers. Then someone tries to implement an application that uses two GPU's to view two separate windows, or whatever. So why enforce that there must be only one instance? Just because you can't imagine the uses for multiple instances, doesn't mean it should be impossible. There might be people more creative than you.

Yes, but the example of the OP was that of a library. So, you might indeed have the case where a really creative person tries to create two instances of the renderer. However, the did not realize it might be possible to create two renderers and assume internally that there is only one renderer, making a seconds instance not work.

When not enforcing, it is possible that the library fails in some obscure and subtle manner, totally baffling the user and leading to loads of weird bug reports.

When enforcing, it is clear to the library user that what he wants is not going to work. He can then ask the developer to stop limiting his creativeness and implement the possibility for multiple instances.

Share this post


Link to post
Share on other sites
You don't have any singletons in your example. They are just regular classes which you name singletons - which is false name.

Singleton:

class Singleton
{
public:
void foo()
{
}

static Singleton *getInstance( void )
{
if (m_instance == NULL) m_instance = new Singleton();
return m_instance;
}

private:
Singleton( void ) {}


static Singleton *m_instance;
};

Singleton Singleton::m_instance = NULL;

...

Singleton.getInstance()->foo();


Not to mention that singletons may not be copied in any way, shape or form, so copy constructor is forbidden. Default constructor must be private, since you must prevent construction of Singleton object on demand.

Singletons also can't be destroyed. A singleton *is*. It exists. It has no life-cycle, no scope, no beginning or end.

This is exactly the reason why singletons are so ill-advised. They get misused.

Singletons come from Java. Java only allows objects, not free functions. If you need singleton in C++, do this:

namespace Singleton
{
void foo( void ) {}
}



It has exactly the role, function and design role of a singleton. And if you use java, then don't use singletons at all.

Share this post


Link to post
Share on other sites
The usual cases where only one at a time is valid is where you're talking to an external API which doesn't let you have multiple contexts at the same time (eg. you've only got a single GL state machine which all GL calls affect). For these, I find that usually you can have multiple objects (like a renderer) created, as long as only one is active at any given time.

Once you realise this, some simple static variables can provide you with enough safety:


class Renderer
{
static Object activeRenderer = null;

void start()
{
assert (activeRenderer == null);
activeRenderer = this;

// stuff ...
}

void stop()
{
assert (activeRenderer == this);
activeRenderer = null;
}
}

main()
{
Renderer r1 = new Renderer(); // fine.
Renderer r2 = new Renderer(); // still fine.

// These are all fine
r1.start();
r1.drawFoo();
r1.stop();

// As are these
r2.start();
r2.drawBar();
r2.stop();

r1.start();
r2.start(); // Asserts, a renderer already active
}


This reads a little more senibly with actual code, where the start() and stop() methods are actually for setting up and tearing down the basic drawing state ( perspective projection, camera position, etc). You may also wish to switch the asserts for throwing an exception if you want it to be a bit more robust.

Share this post


Link to post
Share on other sites
Well if you don't want users to create more than one instance of a class you can enforce it by using Singleton.

Here's an example:

Header

#ifndef _H_TEXTUREMANAGER_
#define _H_TEXTUREMANAGER_
class TextureManager
{
public:
TextureManager();
~TextureManager();

static TextureManager *GetInstance();

private:
static TextureManager *mSingleton;
};
#endif // _H_TEXTUREMANAGER_


And the source


#include "TextureManager.h"


TextureManager *TextureManager::mSingleton = NULL;

TextureManager::TextureManager()
{
assert( mSingleton == NULL );
mSingleton = this;
}


TextureManager::~TextureManager()
{
assert( mSingleton != NULL );
mSingleton = NULL;
}


TextureManager *TextureManager::GetInstance()
{
return mSingleton;
}


Now when you want to use this class, you can create an instance of it by doing

TextureManager *TextureManager = new TextureManager();


BUT when you want to access it from another class you do TextureManager::GetInstance() instead of creating another new class.

If you create ANOTHER instance of the class it will fail, it will give you an assertion error.

I hope this helped you :).

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Singletons come from Java. Java only allows objects, not free functions. If you need singleton in C++, do this:

You can still have static methods within a class though, which are essencially the same thing as free functions, you just get them wrapped up in an extra class namespace is all. The Math class would be a perfect example.

Share this post


Link to post
Share on other sites
Antheus - your reply suggests that you either didn't look at my code or misunderstood it. First of all, copy/assignment are prohibited (look at LocalSingleton.h). Second of all, your code is an example of a "regular" singleton while what I'm proposing is a "local singleton", i.e., a class that doesn't have global access and doesn't necessarily exist for the duration of the program, but one that can have at most one instance at any given time.

TheGangster - The whole point of this is to avoid using the normal singleton. Re-read my post.

OrangyTang - Your code might be suitable for some situations but for my texture manager example it would look wierd and will make it more difficult to use the texture manager while with my code it is transparent and, in my opinion, more generally applicable. But again, I said that my code is (potentially) useful only in certain situations, and in some cases there might be better alternatives.



Share this post


Link to post
Share on other sites
First off Gage... a year ago I would have agreed with you.. but then I used some libraries that were singleton... and well I don't like singletons anymore

Quote:
Original post by Gage64
Consider the example of a texture manager. One of it's purposes is to make sure that a texture isn't loaded more than once, and it does that by maintaining a list of all the loaded textures. If you create 2 texture managers and load the same texture with each one then the texture will be loaded twice and this wastes memory. Granted, this isn't exactly a disaster, but why not prevent it if you can? There might also be better examples than this one.

Quote:

Just because you can't imagine the uses for multiple instances, doesn't mean it should be impossible. There might be people more creative than you.


I'm sure there are, I'm just saying that this can be useful in certain situations. Also, if you'll want to allow for multiple instances later on, it is very easy to do (just remove the inheritance).


First off with the texture manager, what if I want two texture managers? What if I want one texture manager to manage textures for my GUI and another for my ingame graphics? That way when the game starts up I load everything for the GUI texture manager.. and then when the game starts I load the ingame graphics into a DIFFERENT (meaning that singleton can't do it) texture manager, so when the game ends and I want to fall back to only GUI graphics, I just unload the ingame graphic texture manager which is a 100x easier to call "delete gameTexManager;" rather than to individually pick out images..

The problem with singletons is you might not think that there is a need for multiple but some people have an interesting idea that you never thought of before.. but you library can't support it because it is singleton.

And finally, if it as simple as removing interface.. why do you have it? I am willing to bet that if you make the objects singleton, then you will abuse that throughout your library meaning that to remove singleton... you would have to remove inheritance AND change many things in the library to support the new method. Just save yourself early.. don't do it.

Share this post


Link to post
Share on other sites
Quote:

it seems to me that it would be nice to enforce the second one.

It would be nice to enforce the fact that you should not dereference null, either, but you can't. I addressed this point here; the summary is basically, if you have enough self-control not to intentionally dereference null pointers, you have enough self-control not to create random instances of your proposed "local singletons" all over the place.

You're still mixing the "I only want one" and the "I want to protect myself from myself" justifications for singletons. Granted, we're all aware programmers are not infallible but there are limits to the amount of protection you should try to foist upon yourself or your client programmers before things become unreasonable.

You have to assume the people using your code want to do the right thing and are trying their best to do so (which means they should have at least read the class-head documentation that says, "you probably should not need to create more than one of these objects"). If they don't want to do the right thing, they win, period, end of story. They have your code. You can't stop them and there is no reason to worry about it.

Share this post


Link to post
Share on other sites
Quote:

Original post by jpetrie
It would be nice to enforce the fact that you should not dereference null, either, but you can't


But here you can, so why not do it?

Quote:

if you have enough self-control not to intentionally dereference null pointers, you have enough self-control not to create random instances of your proposed "local singletons" all over the place.

You're still mixing the "I only want one" and the "I want to protect myself from myself" justifications for singletons. Granted, we're all aware programmers are not infallible but there are limits to the amount of protection you should try to foist upon yourself or your client programmers before things become unreasonable.


But that's one of my points - I don't think this is unreasonable or beyond the limit you mention. This is something that is very easy to implement and works transparantly. And besides, when has enforcing assumptions through code became a bad idea? You do it all the time using assert() (including to guard againt dereferencing null[smile])

Quote:

Original post by DReed
if it as simple as removing interface.. why do you have it? I am willing to bet that if you make the objects singleton, then you will abuse that throughout your library meaning that to remove singleton... you would have to remove inheritance AND change many things in the library to support the new method


Only if you choose to implement your classes that way. A local singleton class doesn't have to depend on the fact that it is a local singleton and my texture manager class is one such example. Removing the inheritance will not break anything in this case.

About your example of the texture manager needing more than one instance, it is possible to implement it differently than how you desribe .For example, using texture "groups" where when you load a texture you specify what group it belongs to and then you can easily unload an entire group of textures. With this method, you get what you want but you don't lose the protection that the LocalSingleton class offers you.

Share this post


Link to post
Share on other sites
Quote:

But that's one of my points - I don't think this is unreasonable or beyond the limit you mention. This is something that is very easy to implement and works transparantly. And besides, when has enforcing assumptions through code became a bad idea? You do it all the time using assert() (including to guard againt dereferencing null)

That's not the kind of dereferencing null I'm talking about. I'm talking about this:

int *p = 0;
*p = 10;

This is legal syntactic C++ and will pass the compiler. It will fault at runtime. It is considerably stupid to do, but there is no way to protect yourself from doing it other than your own self-control.

There is a difference between asserting preconditions (a good thing, in general) and your "local singleton." Preconditions exist to provide context for code; they provide a way to say "this object will function properly assuming the following conditions are met." You're saying "this object will function properly assuming there is only one." This is an unneccessarily restrictive precondition, because the object would probably function just fine if there were multiple.

Your "local singleton" simply provide implementation-level improvements over the traditional singleton -- it's slightly easier to refactor away the singularity if you need to, but the same could be said of the monostate or other similar patterns.

Also, you're placing lifetime control in the object instead of in the object's context. I would submit that a better way to enforcing a fixed number of instances, if you feel you must "protect" yourself in this fashion, is to place that determination in some kind of factory or other object-creation subsystem. Objects that control their own lifetime are often inflexible in practice, and do not scale well.

Share this post


Link to post
Share on other sites
Quote:

Original post by jpetrie
That's not the kind of dereferencing null I'm talking about. I'm talking about this:


int *p = 0;
*p = 10;


This is legal syntactic C++ and will pass the compiler. It will fault at runtime. It is considerably stupid to do, but there is no way to protect yourself from doing it other than your own self-control.


I think my code can be changed somehow to generate a compile-time error, but even if not, the difference from the above example is that the error my code generates is fairly clear (and can be made clearer) while in your example the error can be triggerd anywhere (whereever the first dereference will happen) and can be hard to track down.

Quote:

There is a difference between asserting preconditions (a good thing, in general) and your "local singleton." Preconditions exist to provide context for code; they provide a way to say "this object will function properly assuming the following conditions are met." You're saying "this object will function properly assuming there is only one." This is an unneccessarily restrictive precondition, because the object would probably function just fine if there were multiple.


Depends on what you consider to be "just fine". Look at my first reply (to spoonbender).

Quote:

Your "local singleton" simply provide implementation-level improvements over the traditional singleton -- it's slightly easier to refactor away the singularity if you need to, but the same could be said of the monostate or other similar patterns.


I would say a LOT easier to refactor away, but maybe I'm wrong. Also, if it's just a small advantage, what's wrong with that?

Quote:

Also, you're placing lifetime control in the object instead of in the object's context. I would submit that a better way to enforcing a fixed number of instances, if you feel you must "protect" yourself in this fashion, is to place that determination in some kind of factory or other object-creation subsystem. Objects that control their own lifetime are often inflexible in practice, and do not scale well.


Objects implementing LocalSingleton don't control their lifetime - they can be created and destroyed at any time (provided there isn't another instance at the same time), so I don't think this takes away any flexibility. Maybe I misunderstood what you wanted to say about using a factory, in which case I would love to see an example.

Also, even in that case this can still protect against creating another instance directly, rather then by using the factory. I know you will say this protection should be replaced by self-control and relying on the user to listen to documentation stating not to do it (in case the class is part of a library like I mentioned above, but even if it's not), but I still say it is better to enforce something like this. Maybe this will convince you further (look at pages 9-10).

Share this post


Link to post
Share on other sites
Quote:

I think my code can be changed somehow to generate a compile-time error, but even if not, the difference from the above example is that the error my code generates is fairly clear (and can be made clearer) while in your example the error can be triggerd anywhere (whereever the first dereference will happen) and can be hard to track down.

I think you are missing the point of this example. It's really not about the detectability of the error at compile-time versus runtime. It's not about the error at all. It's about the fact that you, as a programmer, must operate under the assumption that a programmer using your code -- whether that be you or a client or a co-worker -- is not trying to subvert the system. If you try to protect yourself and your code from malicious misuse, you will fail, and in the process of failing you will probably make your API more unpleasant for those programmers who want to use it correctly. You need to assume that your client programmers want to do the right thing, and you must cater to that. This involves, in some cases, asserting on preconditions and stupid-proofing certain aspects of your code with other code -- making your API easy to use correctly and hard to use incorrectly. But that can only go so far -- there is a line you can cross, the line where the preconditions cease to become beneficial and start becoming unnecessarily restrictive. For anything that must be protected beyond that line, you must rely on external, non-code methods (such as documentation).

Protecting yourself from creating more than one instance, when the class can be usable that way, is an example of crossing that line. It is unnecessarily restrictive.

Quote:

I think my code can be changed somehow to generate a compile-time error,

I only see ugly macro hackery as a possibility, and that would basically ensure you create the object exactly once, effectively giving up the ability for your local singleton to have multiple instances as long as only one is alive at a time.
Quote:

Depends on what you consider to be "just fine". Look at my first reply (to spoonbender).

Do you mean this?
Quote:

Consider the example of a texture manager. One of it's purposes is to make sure that a texture isn't loaded more than once, and it does that by maintaining a list of all the loaded textures. If you create 2 texture managers and load the same texture with each one then the texture will be loaded twice and this wastes memory. Granted, this isn't exactly a disaster, but why not prevent it if you can? There might also be better examples than this one.

This is not a good example. DReed had already posted a plausible use of multiple texture managers.

You are correct that a common goal of a texture manager should be to make sure it does not contain more than one copy of a particular texture. However, that does not mean that you cannot have multiple texture managers. There is no reason to force yourself to have only one. Unnecessarily restrictive. Your example has a critical flaw, this sentence: "if you create 2 texture managers and load the same texture with each one then the texture will be loaded twice and this wastes memory." Why did you create two texture managers? Did you need two texture managers? Or did you do it accidentally? In the case of the former, you cannot use a singleton in any form. In the case of the latter, you have committed operator error -- the same kind of operator error as dereferencing an explicit null. Perhaps it's not quite as obvious, but it's still your fault; it stems from a failure to understand the system you were working in, and that needs to be addressed by training, not by pointlessly restrictive code. Not all software development problems are solved by software.

Quote:

I would say a LOT easier to refactor away, but maybe I'm wrong. Also, if it's just a small advantage, what's wrong with that?

It is potentially quite difficult to remove, because it is a precondition. The purpose of a precondition is to establish safe working conditions; to establish a known state, so that the related code knows what is and is not safe, and what must be checked and what can be assured to be safe. Since the singularity precondition is part of the public interface, it can be relied upon. The second that precondition is removed, code that relies on that singularity precondition might break. You cannot make the argument that you should not rely on the precondition to always be there, because that completely invalidates the concept of preconditions. And you cannot make the argument that you should not rely on that specific precondition to always exist, or you should not make it a precondition to begin with.

Your local singleton makes the concrete code a little easier to refactor, but does not make the potential logical code modifications any easier than a regular singleton or a monostate.

Quote:

Objects implementing LocalSingleton don't control their lifetime - they can be created and destroyed at any time (provided there isn't another instance at the same time), so I don't think this takes away any flexibility.

They place restrictions on their construction, and construction is part of lifetime. Thus, they control their lifetime. Controlling their lifetime provides them an additional responsibility, violating the single responsibility principal and injecting extra dependencies and unnecessary restrictions.

Quote:

Also, even in that case this can still protect against creating another instance directly, rather then by using the factory. I know you will say this protection should be replaced by self-control and relying on the user to listen to documentation stating not to do it (in case the class is part of a library like I mentioned above, but even if it's not), but I still say it is better to enforce something like this. Maybe this will convince you further (look at pages 9-10).

I am quite aware of Liskov's principal and I fail to see what kind of bearing it has on this discussion. Are you referring specifically to the small paragraph in which the author notes that the conventions he established for his original solution to the persistent set problem were violated? That's hardly a compelling argument for your local singleton.

Programmers who will willfully violate the conventions established by their team are bad programmers. Teams that will not accurate communicate the requirements and conventions of their team to new and existing programmers are bad teams. Software is not a solution for broken wetware. Again, there are cases for establishing sanity checks, but they can only go so far. Without know too much of the details of the persistent set issue, I can't say much one way or another about that situation, but I can say that any kind of arbitrary, "just because I'm afraid of myself" singularity restrictions are crossing the line.

In the situation where a Factory object produces Product objects, there are arguments for and against the ability for the Product to be constructible without the Factory. The safe default is to allow it, because the YAGNI principal advocates avoiding implementing unnecessary "features," and restrictions are in this case "features." If the specific problem at hand would be better served by disallowing it, the construction mechanisms of Product can be made private and friendship can be granted to the Factory. This is coupling, but that is exactly what you've decided is the best solution to the problem, in this case. However, solutions that involve extra coupling (since coupling is generally bad) should be implemented as solutions to specific problems, and not as defaults.

So far all of your arguments or justifications for why this kind of restriction is good have fallen into the category of, basically, "I'm afraid and I don't trust myself." Yes, this can be taken too far. I'm not saying the no preconditions should ever be written via code and that all conventions must be documented but not enforced, or anything. But I am saying that enforcing that kind of precondition on the scope you are suggesting (with large, overarching objects like management classes), is just as bad as trying to enforce you don't "accidentally" create two loop iteration variables. If a programmer really cannot trust themselves or their team that much, if they're that afraid that they will, in a fit of pure software development rapture, accidentally create a second texture manager and then accidentally use it all over and then accidentally fail to notice the inconsistencies at runtime, then that programmer doesn't have any business working on a project of that scope.

Are you afraid?

Share this post


Link to post
Share on other sites
In my estimation, any "pattern" that doesn't exist to solve a user's problem but instead exists because the author of the pattern doesn't respect the user's ability is a waste of time.

Gage64, write your Local Singleton. I and others will simply disdain it, because it's entire raison d'être is your disdain for us as your potential client developers.

Share this post


Link to post
Share on other sites
As long as the class in question truly can't have multiple instances--as in, inevitable hardware dependencies will make it not work correctly--then I'm all for this pattern, and have seen it used more than once in the past, to the benefit of the application. If you're just trying to convince users that they shouldn't, then cut it out.

Share this post


Link to post
Share on other sites
If I'm too stupid to read documentation then I shouldn't be programming and I should get what I deserve... A crash. The chance of this making more work for me as a middleware author (assuming that's what this is if it's a library) is doubtful or at the worst rare.

If your users are this dumb I seriously hope you're charging a LOT for support :)

Otherwise, like Sneftel said, if this is a hardware limitation fine (then use a singleton not this local one). Otherwise, drop it.

Share this post


Link to post
Share on other sites
Sneftel - Thank you very much, this is part of what I was trying to say - There COULD be situations where this can be useful.

jpetrie - I wasn't talking about malicious use, but more like what is described in the article I linked (in the paragraph you mentioned). I thought that was a good example of what I'm talking about but you seem to disagree.

In fact, this class was written in the spirit of "Make classes easy to use and hard to misuse". I'm still not really convinced it is unnecessarily restrictive, but again, maybe I'm wrong.

Also, why would you use this on a class that can be usable without it? Obviously use it if you need it, and don't use it if you don't.

Quote:

I only see ugly macro hackery as a possibility, and that would basically ensure you create the object exactly once, effectively giving up the ability for your local singleton to have multiple instances as long as only one is alive at a time.


You're not supposed to have more than one instance - what you're talking about is something like what OrangyTang did with his Renderer example, which is not what I'm suggesting here.


This is not a good example. DReed had already posted a plausible use of multiple texture managers.
[/quote/

I have already commented on his post, but maybe a texture manager is not the best example for this.

Quote:

In the case of the latter, you have committed operator error, ..., and that needs to be addressed by training, not by pointlessly restrictive code.


An error this can protect against, and I would hardly say it's pointless. But again, maybe a texture manager is not a good example.

Quote:

Original post by Oluseyi
In my estimation, any "pattern" that doesn't exist to solve a user's problem but instead exists because the author of the pattern doesn't respect the user's ability is a waste of time.


Then why do programming laguages have constructors, destructors, access specifiers, etc. if not to ensure a programmer doesn't do something he shouldn't do (forget to call an init()/shutdown() function, access a private variable it shouldn't touch, ...)?
This class just tries to make it harder for you to shoot yourself in the foot. I assure you I have no disrespect or disdain for anyone.


I feel like in the last few replies we have been going in circles (or I'm just being misunderstood) so I want to say one more thing:
I didn't write this class for myself or anyone else and I'm actually not using it. Also I'm not a professional programmer and I'm not writing any library. I just had this idea and decided to share it with others on this forum to see what they think. I guess most people think it's useless so maybe I should just stop replying and let this topic die.

I actually have very little programming experience (less than you think but I don't want to specify[smile]), and that's probably why I'm unable to come up with good examples for this class, and don't fully understand the repercussions of using it. Maybe after I gain more experience I will see things your way or be able to defend this "pattern" better. Until then, I will probably keep replying as best as I can, and will keep hoping someone will find this useful (and hopefully won't ruin his code as a result[smile]).

Share this post


Link to post
Share on other sites
I've been writing a lot of stuff assuming I already linked my singleton article... but apparently I had not. I suggest you read it, it goes into some of my points in more detail than I've expounded upon here.

Quote:

Also, why would you use this on a class that can be usable without it? Obviously use it if you need it, and don't use it if you don't.

Ah, but there's the rub. The big problem with arguments concerning the benefits of singleton or singleton-like patterns is that they fall back on comments like this. Yes, the use of your singleton class is justified in this case: if the class needs to be a singleton, it should be a singleton. You and I and others all agree on this. When bolstered by the supporting argument that you only apply your local singleton when the class in question should be a singleton, your original argument concerning the benefit of your local singleton pattern is infallible.

Except when should the class be a singleton?

This the problem: most arguments supporting singletons rely on arguments that are themselves flawed, meaningless, or otherwise empty or endemic of larger problems. Most software development problems cannot be considered in isolation, and this is no different. When should the class be a singleton? Rarely. As Sneftel suggested, the main situation is when the class in question has hardware dependencies or other unavoidable external requirements that make it impossible to implement in any other fashion. This is, in practice, quite rare. What usually happens is that people trick themselves into thinking a class must be singleton when really they just want it to be a singleton, because they are afraid of making more than one instance or they don't want to take the extra effort to design the class well so that it does not have the singularity restriction. In practice, removing such restrictions is almost trivial. The fewer restrictions a class has on its usage, the more general purpose and useful (and thus, reusable) it is. The singularity restraint removes a lot of that utility, and adds a lot of disadvantages, for an almost nonexistent benefit (a small measure of "safety"). This unbalanced ratio of cost/benefit easily throws the singleton over that aforementioned line.

Quote:

You're not supposed to have more than one instance - what you're talking about is something like what OrangyTang did with his Renderer example, which is not what I'm suggesting here.

I said your local singleton has the ability to "to have multiple instances as long as only one is alive at a time," and you said "they can be created and destroyed at any time (provided there isn't another instance at the same time)." This is the same thing. However, compile-time checks that you only create a single instance would remove that feature. I don't think its necessarily a useful feature at all, I'm just pointing out that if you did implement compile-time checks that you were creating only one instance, ever, your local singleton would be even more similar to the traditional singleton pattern (and slightly more restricted than it is now). That's all.

Whether or not you intended your local singleton to work like Orangy's or not, it is basically the same thing. His is slightly more explicit, but I can achieve the exact same behavior with yours.

Quote:

but maybe a texture manager is not the best example for this...
An error this can protect against, and I would hardly say it's pointless. But again, maybe a texture manager is not a good example.

A texture manager is a bad example. There are pretty much no good examples for legal singletons in game development -- no objects that are impossible to model without enforcing singularity. This is in some ways an extension of the fact that object models attempt to be metaphors for reality. And there is nothing in reality that has exactly one instance.

Quote:

Then why do programming laguages have constructors, destructors, access specifiers, etc. if not to ensure a programmer doesn't do something he shouldn't do (forget to call an init()/shutdown() function, access a private variable it shouldn't touch, ...)?

Such constructs exist to tell you how you should use something, not how you shouldn't. Subtle difference. See in my article under the section "It doesn’t make sense to have more than one FoobazManager," the passage discussing the cell metaphor. The context of the object should determine if a single instance is needed, not the object itself. If the object does so, it becomes less reusable (without modification).

Quote:

I feel like in the last few replies we have been going in circles (or I'm just being misunderstood)

I think you're being understood well enough. I also think you're being a little stubborn with respect to clinging to the "protection" argument. But that is understandable, because it is very difficult to make arguments against singletons -- primarily because that involves deconstructing the arguments for singletons, which (as I noted above) are multilayered. You attack one argument and peel it away, and the singleton-proponent falls back to another. Rather quickly, the argument has moved well beyond the singleton itself. This is usually because the problem is not the singleton, conceptually. The problem is usually the flawed design or thought process that led the programmer to the decision that the singleton was good (the idea that it protects you, the idea that you need -- but really want -- only one, and so on). The singleton is just the proxy, which is why I and others have more focused on attacking your rationale and "developmental ideology" than the class itself.

Quote:

I actually have very little programming experience (less than you think but I don't want to specify), and that's probably why I'm unable to come up with good examples for this class, and don't fully understand the repercussions of using it. Maybe after I gain more experience I will see things your way or be able to defend this "pattern" better. Until then, I will probably keep replying as best as I can, and will keep hoping someone will find this useful (and hopefully won't ruin his code as a result).

I don't want to sound harsh, but perhaps you should consider the wisdom of arguing software design with established software developers with years of experience?

I was in your position once, too. I imagine most of us were. I've been programming for well over half my lifetime (I'm twenty-five), and it took me years to truly see the flaws inherit in the singleton pattern and, more specifically, with its misapplication. It took me even longer to finally rid myself of the dogmatic and process rot they imposed on my development methodologies. They are nasty little patternses(*). As much as I'd like to be able to present these arguments against singletons and have you turn away from their misapplications, I recognize that it's not that likely.

First, because I'm not that good at providing appropriately convincing counterarguments, and second, because most of the arguments presuppose some understand and appreciation of the value of certain software design truths that simply come from experience -- the benefits of maintainable code can never really be fully understood until you've had to maintain a multi-million-line monster of a project littered with poorly-thought-out, tightly-coupled singletons and other nastiness that would put your hair on end. Experience -- getting burned -- seems to be one of the most effective arguments against singletons (and other bad software development practice). So the best I can hope for is that I'm annoying enough that you remember what I've said and drop the damn singletons sooner, rather than later.

Quote:

I guess most people think it's useless so maybe I should just stop replying and let this topic die.

If you just let it die, you won't learn anything from it. It's not assured you'll learn anything from it immediately anyway, for reasons I just detailed. But perhaps...

(*) It's Gollum, get it?

Share this post


Link to post
Share on other sites
One of the dangers of the singleton pattern is that it encourages the developer to not make important design decisions and allows them to write lazy code on the assumption that "there will never be more than one". By reaching for the singleton pattern so soon, they are essentially allowed to skip the question "Should this really be a singleton?" in the first place. This can later become one of the pains of refactoring when they find out that they actually need two of their singleton; not only do they have to work out access to each of the new instances, lifetime issues, etc, they also have to re-write the former-singleton itself, and likely, some or all of its dependants and dependancies.

I did, in fact, used to be a proponent of the Singleton Pattern. In fact, I was finally convinced of its worthlessness only 2-3 months ago during a similar discussion, and have literally deleted it from my toolbox of code snippets. If I ever find myself feeling the need for one again, I have to hop on the net to find an implimentation (to make sure I've re-implimented it correctly) and copy it into my codebase. I don't expect that I'll ever feel that temptation, but I've made this decision to literally force myself to evaluate the need. The development world would probably be better off if all references to "Singleton" were banished from the internet, and one would have to drive to the local library or delve into the depths of the University Archive to find such information.


You should really ask yourself the question "Is it better to protect myself and my users from the harm that the Singleton Pattern encourages, or to protect them from creating two instances?"

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Singletons come from Java.


Sorry to say, this isn't true. The GOF book used C++ and Smalltalk for its code samples. They might well be ridiculously *popular* in Java code, but they weren't invented by the Java community, nor are they anything like necessary in Java - there is an equivalent to the C++ namespace trick, namely 'static' abuse. And, wouldn't you know it, the Java standard library uses this trick: see for example the Math class.

Share this post


Link to post
Share on other sites
Quote:
Original post by Gage64
Then why do programming languages have constructors, destructors, access specifiers, etc. if not to ensure a programmer doesn't do something he shouldn't do (forget to call an init()/shutdown() function, access a private variable it shouldn't touch, ...)?

First, many languages that have these conveniences don't prevent you from messing around with attributes in more direct fashion. (My current favorite language, Python, provides no real data hiding or access restrictions/enforcement.) Secondly, even languages that do attempt to restrict you can be hacked - and in some cases trivially so - by a sufficiently determined programmer.

Those features are conveniences. As a convenience, the option for the client programmer to restrict the number of instances of a type that may exist is cool; as a restriction decided on by the API author, decidedly not cool. Savvy?

Share this post


Link to post
Share on other sites
You guys mentioned various times in this thread that you guys would make a check at compile time to make sure its singular...

How would you do that? (This is mostly out of curiousity)

Share this post


Link to post
Share on other sites
Quote:

TheGangster - The whole point of this is to avoid using the normal singleton. Re-read my post.


Sorry misunderstood your idea.

By using "your way" you will never know what the instance is, you will only be able to know that there is an instance and that it can be deleted.

jpetrie - I understand that you and others don't quite like Singletons, but I am still new and I would like to learn a bit more about it, why not use it?

I agree that sometimes it's misuse, but other times it's necessary to simplify things, or what am I not getting?

Share this post


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

  • Advertisement