Would this be proper usage of Singletons?

Started by
11 comments, last by CrimsonSun 15 years, 6 months ago
Hello @all Now, before we start, I do realise that Singletons are not popular here in the beginners forum. A lot of beginners use them improperly, using wrong justifications. I know the skillful people here like to link us beginners to articles. I've read this particular one. Still, is there absolutely no justification to use a Singleton?? I'm having trouble with my project. My project is basically a collection of classes and functions that will provide a stable base for future games/tech-demos I plan to write. One part of this project is the Log. I've decided to use a single Log, that notifies of major events (important initializations) and errors/warnings. Everything needs to access my FoobazManager There can just be one FoobazManager The two most popular reasons for Singletons, right there. However, the article suggest these don't justify Singletons. The thing is, I really hate how it seems like the Log is penetrating every function as an argument, just because those functions could do with some logging. This is especially the case with constructors. I wanted to ask if there is maybe other good articles that explain "proper" use of Singletons. Maybe some of the professional developers on this board could throw in their opinion on Singletons as well, I always value advice from the experienced :D --Rob
Advertisement
Do you have many global functions?
If all your functions are in classes, then you only have to pass the log to the constructors, and store it as a member variable for member functions to use. You say you especially don't want to pass it to constructors, but in my opinion that's the best usage. You construct the object, with knowledge of the log it's supposed to use for the rest of it's lifetime.

If you have several global functions.. then either change the design and put the functions in objects, or just make a global pointer to the log and get on with your program, instead of letting it slow you down.

You could still create your log as a normal object, not a singleton, and pass it to all your objects. Just that you also set a global variable to point to the same log, and let the global functions use that. Perhaps not a very elegant solution, but it's fast, it works, and if you ever want to put those functions into objects later, it's easy to change the usage of that global pointer to a member pointer instead.
Quote:There can just be one FoobazManager
That sentence is incomplete. In OO, one speaks of cardinality: "There must be four wheels per car." "There must be zero or more classes per student." "There must be zero or one helmet per character." So complete that sentence: "There can just be one FoobazManager per..."
Quote:Original post by c4c0d3m0n
Hello @all


Now, before we start, I do realise that Singletons are not popular here in the beginners forum. A lot of beginners use them improperly, using wrong justifications. I know the skillful people here like to link us beginners to articles. I've read this particular one.

Still, is there absolutely no justification to use a Singleton??


I'm having trouble with my project. My project is basically a collection of classes and functions that will provide a stable base for future games/tech-demos I plan to write. One part of this project is the Log. I've decided to use a single Log, that notifies of major events (important initializations) and errors/warnings.

Everything needs to access my FoobazManager
There can just be one FoobazManager

The two most popular reasons for Singletons, right there. However, the article suggest these don't justify Singletons.


The thing is, I really hate how it seems like the Log is penetrating every function as an argument, just because those functions could do with some logging. This is especially the case with constructors.

I wanted to ask if there is maybe other good articles that explain "proper" use of Singletons. Maybe some of the professional developers on this board could throw in their opinion on Singletons as well, I always value advice from the experienced :D


--Rob


The article doesn't shoot down the "There can just be one instace of .." argument, it shoots down the "It doesn't make sense for there to be more than one instance of .." argument.

There is a huge difference between the two. If you cannot create more than one instance without breaking things then using a singleton makes sense , It doesn't make sense to restrict a class to a single instance just because you belive that a single instance is enough.

If you need a single instance of your log class that can be accessed from anywhere then you should create a global instance of the log class.
[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!
Alright... I made a thinking error it seems. Singleton-ness isn't really needed. I need one Log per excecutable ;) It was a design-decision I took, to have just one Log for everything.

A global Log is the real priority though. Indeed, having more than one Log couldn't hurt, seeing it's just a glorified std::stringstream with some extra functions.

So, in theory, I could just create a global Log and use that for everything. That changes my question to:

What's the problem with globals? Why are they frowned upon? Is this a justifiable case to use a global considering I want to be "doing things the right way"?
Quote:Original post by c4c0d3m0n
Alright... I made a thinking error it seems. Singleton-ness isn't really needed. I need one Log per excecutable ;) It was a design-decision I took, to have just one Log for everything.

A global Log is the real priority though. Indeed, having more than one Log couldn't hurt, seeing it's just a glorified std::stringstream with some extra functions.

So, in theory, I could just create a global Log and use that for everything. That changes my question to:

What's the problem with globals? Why are they frowned upon? Is this a justifiable case to use a global considering I want to be "doing things the right way"?


The problem with globals is that it can easily get messy, you generally want your variables to have as small a scope as possible, that said even the C++ standard library uses globals, just look in iostream.h and you'll find:
namespace std {extern istream cin;extern ostream cout;extern ostream cerr;extern ostream clog;extern wistream wcin;extern wostream wcout;extern wostream wcerr;extern wostream wclog;};


notice however that the globals are enclosed in a namespace (this makes it less messy), doing the same with your log instance is probably a good idea, (Thus its YourEngine::GlobalLog or somthing similar to access it)
[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!
Globals are frowned upon because, in almost every instance, when someone thinks that there should be "one ____ per executable" to use terminology found in this thread, there is actually a more refined association that can be made that would eliminate problems associated with the design. For example: One texture manager per executable. Sounds nice, you throw all your textures into one big pit and that's that. Wander on down to many DirectX forums that also cater to us multi-monitor setup people, and it becomes very apparent that a "One texture manager per screen", or "one texture manager per physical adapter" type of model solves a lot more headaches. In NEARLY all cases, this sort of thing crops up, and the use of globals frequently shows a lack of foresight or a weakness of design.

There is also the issue of who gets to use the various parts. Debugging becomes easier when you know for a fact that only a small handful of your classes will be dealing with something. This means problems can only be originating from a small area when something goes wrong. For example, game logic codes doesn't need access to my texture management scheme.

Despite all the arguments against global structures, there are some spots where they are useful [which are pretty much the spots where nothing else does an outright better job]. Debug logging is really one of the last and only things I use global functions for....
Hmm... Alright. I've decided on making the Log a global (namespaced) object. One could still create a seperate Log for a special occasion, although it would get messy in the Console possibly... I'll implement a way around that later.

I have a problem though. Even though I'm using including-guards, my linker is throwing all kinds of multiple-definition errors at me.

/**  * @author             Rob Spoel  * @copyright          © 2008  *  * @file               R2e/log.hpp  ***/#ifndef _R2E_LOG_H_#define _R2E_LOG_H_#include <sstream>#include <string>namespace R2e {class Log{  public:    Log();    ~Log();    void  write( std::string );    const std::stringstream& read() const { return m_log; }    void open_sub () { depth++; }    void close_sub() { depth--; }  private:    std::stringstream m_log;    unsigned int depth;} log; // class Log} // namespace R2e#endif // _R2E_LOG_H_


rob@rob-laptop:~/Development/R2e$ make testg++ -c test/main.cppg++ -c R2e/engine.cppg++ -c R2e/gfx.cppg++ -c R2e/log.cppg++ `sdl-config --cflags --libs` -lSDL_image main.o loading.o menu.o engine.o gfx.o log.o -o applicationengine.o:(.bss+0x0): multiple definition of `R2e::log'main.o:(.bss+0x0): first defined heregfx.o:(.bss+0x0): multiple definition of `R2e::log'main.o:(.bss+0x0): first defined herelog.o:(.bss+0x0): multiple definition of `R2e::log'main.o:(.bss+0x0): first defined herecollect2: ld returned 1 exit statusmake: *** [test] Error 1


Is there something dodgy about the way I defined the global?
Don't do

class Log
{
} log;

in a header. You shouldn't create an instance of Log in the header like that. Instead, do this:

// header
class Log
{
};

extern Log log;

// in .cpp source file

Log log

The problem is that every file that includes Log.h is recreating the log variable.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]
Quote:Original post by c4c0d3m0n
Hmm... Alright. I've decided on making the Log a global (namespaced) object. One could still create a seperate Log for a special occasion, although it would get messy in the Console possibly... I'll implement a way around that later.

I have a problem though. Even though I'm using including-guards, my linker is throwing all kinds of multiple-definition errors at me.

*** Source Snippet Removed ***

rob@rob-laptop:~/Development/R2e$ make testg++ -c test/main.cppg++ -c R2e/engine.cppg++ -c R2e/gfx.cppg++ -c R2e/log.cppg++ `sdl-config --cflags --libs` -lSDL_image main.o loading.o menu.o engine.o gfx.o log.o -o applicationengine.o:(.bss+0x0): multiple definition of `R2e::log'main.o:(.bss+0x0): first defined heregfx.o:(.bss+0x0): multiple definition of `R2e::log'main.o:(.bss+0x0): first defined herelog.o:(.bss+0x0): multiple definition of `R2e::log'main.o:(.bss+0x0): first defined herecollect2: ld returned 1 exit statusmake: *** [test] Error 1


Is there something dodgy about the way I defined the global?
A side note: symbols that begin with a leading underscore followed by a capital letter are reserved by the implementation, so they should not be used for header guards (or anything else, for that matter).

This topic is closed to new replies.

Advertisement