Jump to content
  • Advertisement
Sign in to follow this  
Gage64

Local Singleton?

This topic is 4161 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
Sign in to follow this  

  • Advertisement
×

Important Information

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

We are the game development community.

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

Sign me up!