C++ Singleton questions

Started by
26 comments, last by Sneftel 17 years, 2 months ago
Quote:Original post by ZMaster
EDIT: And by the way, I've heard using global variables in an OO design is nearly as bad as using singletons. What about this one? Personally I don't like globals, but this is more a matter of personal preference and its not based on any objective idea.


The problem with a global variable is that any code anywhere can change its state at any time. In order for a piece of code to use a global variable correctly it must know (and keep track of) how all the other code uses it. That can create all sorts of problems and lots of opportunities for bugs.

On the other hand, global variables can be very convenient. Convenience has some value, but probably less than you think.

What does that mean? It means that there is a tradeoff. Understand the pros and cons and you will be fine.

So what about Singleton? It's unfortunate that people here write that Singleton is bad. A Singleton is a safer global variable. Anyone that uses global variables buts argues against Singleton obviously lacks understanding. Lots of smart programmers use Singleton, so there has to be some benefit. People that write that Singleton is bad without explaining how it might be useful are just making religious statements, and not being very helpful.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Advertisement
The more and more these threads come up the more and more they look like debates of (global) shared-mutable state vs referential transparency and so monads keep harping at me as a vital key to a possible end all solution/pattern to singletons.
Original post by Emmanuel Deloget
Quote:Original post by Mantear

Quote:
Quote:Nitage
Think carefully about your design - is your singleton really doing anything that a global variable isn't? Or does it just 'feel' more OO?

I'm not denying that using the singleton is basically using a global variable. If using a singleton is determined to be such an awefull thing, what's the best alternative? I understand the points made so far as to why they could be dangerous/bad to use, but those issues don't seem very difficult to overcome as long as the situations where they are used are limited and well defined.

One advantage I can see right now for using a singleton logger is that it can easily drop into another project without much hastle. Obviously, the output method would need to be modified as needed (writing to a file? to an output stream? etc), but all the message capturing methods could remain the same.

I guess the bottom line is, if singletons are really so horrible, how should I implement my logger instead?


I guess that reading the Singletonitis articles by Washu (1, 2 and 3) should help you [smile].


If I read it right, the third link basically spells out that, according to Washu, a logging system is one of the few acceptable reasons to use a singleton. Which is the exact reason I'm using one.
Original post by Mantear
Quote:Original post by Emmanuel Deloget
Quote:Original post by Mantear

Quote:
Quote:Nitage
Think carefully about your design - is your singleton really doing anything that a global variable isn't? Or does it just 'feel' more OO?

I'm not denying that using the singleton is basically using a global variable. If using a singleton is determined to be such an awefull thing, what's the best alternative? I understand the points made so far as to why they could be dangerous/bad to use, but those issues don't seem very difficult to overcome as long as the situations where they are used are limited and well defined.

One advantage I can see right now for using a singleton logger is that it can easily drop into another project without much hastle. Obviously, the output method would need to be modified as needed (writing to a file? to an output stream? etc), but all the message capturing methods could remain the same.

I guess the bottom line is, if singletons are really so horrible, how should I implement my logger instead?


I guess that reading the Singletonitis articles by Washu (1, 2 and 3) should help you [smile].


If I read it right, the third link basically spells out that, according to Washu, a logging system is one of the few acceptable reasons to use a singleton. Which is the exact reason I'm using one.


Not really. I spelled that out as a possible example, but in reality most logging functionality tends to be far more complex than a logging singleton can provide. Furthermore, as most have pointed out, most languages provide a mechanism (std::clog in C++) that lets you direct to an already available stream to ouput your logging information.

Furthermore, more advanced logging capabilities tend to make implementing it as a singleton far more complex than is needed. It also adds a dependency that should really be avoided for any code that you ever hope to reuse elsewhere. First you end up having to drag along your logging singleton...then your texture singleton because for some reason you made your logger rely on it...which drags along that pesky sound manager singleton, which for some reason relies on the file system singleton...

Singletons and globals both suffer from the same problems really: non-restricted visibility and untrustable state information. In both cases (globals and singletons) you have no real ability to restrict exactly who can and will use them. If you make them globally visible, then they WILL be used in a manner that can't be controlled (even if you're the only programmer) on any moderately sized project.

Now, there is something to be said about winging it, that something in my opinion is "avoid it", but some people think otherwise. Then again, those are the same kind of people who post flawed benchmarks in an effort to support their claims.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

As with most of these discussions, I am one of few people who actually see a place for singletons, even if I don't like them that much.

There really is only one place for them, in a class that will not be affected by the singleton pattern's many downsides. So if you have a class that will not be used (or at least not even peripherally involved with) multithreaded code, that is designed in such a way that the static initialisation problem doesn't matter, and that there is no simpler way of doing it, then by all means, use a singleton. In the *vast* majority of cases there are better ways of doing it, but if there isn't, as long as you know the risks, and are prepared to handle them (I've known projects that were delayed due to unforseen singleton effects), then use it. (Just don't blame me if it blows up in your face).

They should never be used just because it's *easier* to program that way.

That said, I would use a singleton logging class over std::clog, which has platform specific behaviour, and if you're not running *nix (or a console app) you have to redirect to a file anyway.
Quote:Original post by gregs
That said, I would use a singleton logging class over std::clog, which has platform specific behaviour, and if you're not running *nix (or a console app) you have to redirect to a file anyway.


Care to elaborate on what you mean by "platform specific behavior"?

If std::clog were insufficient for my needs, it'd be due to one of the following:

1) Multiple filtered logging categories to care for.
2) Multiple filtered logging levels to care for.
3) Multiple logging destinations to care for.
4) I want to put my initialization in a constructor and forget about it.
5) Other miscellanious boilerplate.

None of these are helped by making the class a singleton instead of just a normal class. None. Zero. The cost? They're quite likely even directly hindered by it. And your immortal soul -- or at least some serious refactoring or mantinence headaches when it turns out you really need 2. Or 3. Or N. Or some other excuse to end up on thedailywtf.com.

Worlds will not collide if two loggers are made. Even if they were (which would require some serious effort on your part), how could you trust your fellow programmers to properly mantain singletons when they can't even read the comment you made by the assert(!already_instantiated); in the constructor to that effect?

Simply put, you can't. These people are timebombs -- either convince management to fire them, or you might as well just end all the misery leading up to your project's inevitable cancellation, the ensuing layoffs, and taint on your resume. Remember: C++ lets you reuse the bullet!

There's one place that I basically use the singleton pattern: dealing with C callback libraries. But I pretend not to. I don't expose a public GetInstance(); interface -- there's only the private member static "instance" variable, and the private static callbacks which are to be registered, and the class registers itself. I create one instance of the class as I would any other, and the constructor asserts there wasn't a previous instance, then sets "instance" to itself. It's likely not even global. If I need multiple instances? Just set up the dispatch framework from whatever get____ID() functions the API exposes, update the (de)constructor(s) to update said dispatch instead of a single instance variable... and poof! No broken code, things just don't explode if you make multiple instances of them!

I find this a lot more productive than going through the bother and effort to mentally work out of it's really reasonable to save a few keystrokes at best at the cost of locking myself into one instance on pain of structural refactor.

[Edited by - MaulingMonkey on February 13, 2007 1:23:50 PM]
Quote:Original post by MaulingMonkey
There's one place that I basically use the singleton pattern: dealing with C callback libraries. But I pretend not to. I don't expose a public GetInstance(); interface -- there's only the private member static "instance" variable, and the private static callbacks which are to be registered, and the class registers itself. I create one instance of the class as I would any other, and the constructor asserts there wasn't a previous instance, then sets "instance" to itself. It's likely not even global. If I need multiple instances? Just set up the dispatch framework from whatever get____ID() functions the API exposes, update the (de)constructor(s) to update said dispatch instead of a single instance variable... and poof! No broken code, things just don't explode if you make multiple instances of them!

Nothing can ruin a nice well designed class like an annoying C callback, I hate having to 'nail' my class to a fixed global just because of it.

Having said that, most good C libraries include a handy-dandy void* user object along with the query. Unfortunately most libraries aren't that good (or even consistant).
Why would anybody ever make a logger a singleton? I often enjoy (well, okay, I often use) the ability to make a second logfile for a certain module or area of functionality. Why would you want to force yourself not to do that?

This topic is closed to new replies.

Advertisement