Advice on global variables

Started by
48 comments, last by Nathan Baum 17 years ago
Quote:Original post by jyk
There are many aspects of singleton usage that are less than obvious (not the least of which is order of construction and destruction).
@The OP: I would at least read this before deciding whether or not to make use of singletons in your own code.

Mmm, I do not want to start a debate here, but I would like to make sure that we are on the same page :
First, the creation of a singleton can easily be controled. Only the *smart* aspect of the singleton (in my example) makes its creation difficult to control, agreed.
Second, I read the post that you recommended, and I found the following statement to be quite confusing :
Quote:"So how can the programmer guarantee that multiple instances of the object won't be created? Quite simply, you don't."
In my example, the constructor is private (even though I mispelled the class' name), and I believed it should insure that multiple instances could not be created (in a single thread env.), right ?

In my opinion, there are at least two good reasons to use singletons (and thus yes, global objects) :
1) optimization
2) object's scope and lifetime : If the object's lifetime is the same as the whole program, then it makes half sense to be a global object. If on top of that, it has to be accessed anywhere in the code, then it makes the second half sense. Think of a database accessor object for instance.
Cheers
StratBoy61
Advertisement
Quote:Original post by StratBoy61
1) optimization


Dubious. True global objects need synchronization scaffolds to be usable in any industry-sized project, which would hinder performance. The only advantage would be saving on argument-passing, which can never be fully accomplished because of the necessary __thiscall in most cases. And either way, it's easier to make an object global than to un-make it, so profile first, and make global last.

Quote:2) object's scope and lifetime : If the object's lifetime is the same as the whole program, then it makes half sense to be a global object. If on top of that, it has to be accessed anywhere in the code, then it makes the second half sense.


If the object has to be accessed anywhere in the code, then you have a major design problem. The only solution in this kind of situation, if you are serious about writing any remotely maintainable project, is to stop writing code immediately and starting to think about a full redesign of the involved object. The strength of module coupling introduced by project-wide global variables is too strong to bear in many projects.

The usual consequence is that the project bursts into flames in beta-testing, when testers start demanding a feature, and you're faced with either spending a week un-globalizing half the code (and doing nothing else) or refusing the feature altogether. On industry projects, this happens even sooner, when the client changes requirements, and you dive into refactoring for a few days, never to be seen again.

However, if the object's lifetime is the same as the whole program, it makes full sense to make it a global object. Just remember that the problem with globals lies not in creating them, but in accessing them. If your global variable is accessed once in the entire program, and passed as argument everywhere else, everything is fine. Things start to hurt when you have global variable accesses all over the code.

Quote:Think of a database accessor object for instance.


Which is actually a bad idea in a large project, where the database server could be split into several servers to split the load. Yes, I've had this happen, and I was happy to have a protective abstraction layer between the server access and the data usage. If you're confident that your project is throwaway and will not be used in a serious fashion ever, then fine. In the industry, this kind of practice is a big flashy red sign reading "rewrite half the system next year".
Quote:Original post by StratBoy61
In my opinion, there are at least two good reasons to use singletons (and thus yes, global objects) :
1) optimization
2) object's scope and lifetime : If the object's lifetime is the same as the whole program, then it makes half sense to be a global object. If on top of that, it has to be accessed anywhere in the code, then it makes the second half sense. Think of a database accessor object for instance.


Optimization should never be a part of design discussion. Scalability and overhed yes, but not optimization.

The database accessor isn't best example either. Typical SQL accessors revolve around connections and connection specific objects that live only for the duration of the connection. Also, most connections cannot be shared between threads, they need to be recreated if a connection error occurs, and similar. Methods for accessing it are obviously global, but not much else.

Singletons and globals have their place. But in even basic OO application, they should be very limited.

I have ran across an application converted from C into C++. It was fully object oriented. It contained several hundred classes, all singletons, containing C code, and named after the file they were in.

The final decision about what is a singleton and what isn't comes down do separating function from form.

A singleton should also be able to start-up its own internal state at any time for any reason, without external dependencies. If it depends on other objects, then it'll make a poor singleton, since it may cause random failures at most unusual times. If singleton isn't able to perform it's own state initialization, or is initialized manually, then it's not a singleton, just a global variable.

Going by the above example, even if window size won't change during application life-time (is this true?), it will be initialized after some event, and most likely depends on several other pre-conditions (validating the driver, checking for resolution, allocating handle, ...). And that's a lot for a singleton to handle.

int		FPSCount = 0;char*		FPSCountTxt = "";DWORD		timeCurrentTime = 0;DWORD		timeDemoStartTime = 0;DWORD		timeElapsedTime = 0;char*		timeElapsedTxt = "";


This may seem like good candidate for singleton, but it really isn't. There's a special DemoStartTime. So there's a difference on where these variables will be used.

Going back to use of this class. Where will it be called? There's bound to be a render loop that will time the rendering process. And the measurment becomes a member of the window class. Each window has its own measurement. Even more, the fps monitor should be part of scene definition, since this is what we're interested in. Time when scene was started, number of frames rendered, etc. On top of that, you can of course add extra global counter for application life-time.

// Testing Variablesbool		showHelp = FALSE;bool		showWireframe = FALSE;bool		showFog = FALSE;bool		enableLighting = TRUE;bool		enableSound = TRUE;bool		enableFrustum = TRUE;


These are also not globals. In order for these variables to have any effect, the rendering system needs to be between fully initialized and not being destroyed. And that is much less than full application life-time.
These variables are prime candidate for being completely hidden within renderer class, and being changable only though accessors. Even more, this allows you to query device capabilities and log any problems with setting these flags.

bool		MouseMove = FALSE;    int		MouseButton = -1;	//Mouse PositionGLfloat		MoveXcoord, MoveYcoord;		int		clickXcoord, clickYcoord;


Mouse events seem like a good idea for a global. Until you need to write a demo that will simulate mouse moves and mouse clicks. Who will then have the authority, system mouse event driver, or your demo driver? And how will you disable one of them? So these must definitely not be globals of any kind - even more, all these variables are extremly short live, ideally, their life-span 0 seconds, since they become invalid the moment system passes them on. From design perspective, these are prime candidate for either method parameters, or message queue.

There is really very little reeason for globals - ever. I suppose the only ones that really warrant such would be invariants. Everything else goes against OO design (except valid exceptions).

Quote:Original post by derickdong
When people tell you something is bad or wrong (programming-wise), they usually mean that you shouldn't use it unless the alternative is worse. For instance, many sources will tell you that goto should never be used. However, there are times when it can be useful.

Useful, perhaps, but usually simple refactoring is a better alternative.

Quote:In my opinion, there are at least two good reasons to use singletons (and thus yes, global objects) :
1) optimization

Uh?
Singletons are not faster. Globals are not faster either. (True, you save having to push a few bytes onto the stack when calling a function, but in return you lose data locality which will give you cache misses, and the compiler is unable to perform many optimizations on a global because it can be accessed from anywhere, so it has to be written to memory as soon as it's updated. It's no good just holding the new value in a register, because then other parts of the code may just decide to access the obsolete version in memory.

Quote:
2) object's scope and lifetime : If the object's lifetime is the same as the whole program, then it makes half sense to be a global object. If on top of that, it has to be accessed anywhere in the code, then it makes the second half sense. Think of a database accessor object for instance.

I fail to see the connection between lifetime and scope. An objects lifetime may be the same as the program, but that doesn't mean it should be visible to every part of the program. And if something has to be accessed anywhere in the code, you have a really bad design.
I'd certainly hate for all parts of my program to be able to access a database.
WOW,

this is getting a big thread !!

I am trying to follow this, honest.

Good to hear so many views.

Well let's give another example, since Antheus quoted the timing code.

MAIN.CPP
timeDemoStartTime = GetTickCount();while(!done)    {  // main loop  // PeekMessage  // render  // get FPS and times    // process mouse  // process keyboard}


ENGINE.CPP
void getTimes(){  timeCurrentTime = GetTickCount();  timeElapsedTime = timeCurrentTime - timeDemoStartTime;  // calculate FPS}


So surely "timeDemoStartTime" needs to be global ?

Or like some say, passing parameters, not globals.
Do I need a function that returns time.

in MAIN.CPP
timeDemoStartTime = getStartTime();

in ENGINE.CPP
DWORD getStartTime(){ return GetTickCount();}


That could reduce some of the required globals, but not all.

** EDIT

Even if I look at NeHE new beta frame work it has problems.
Ahhhh BETA I hear you shout, well don't count on it being fixed lol.
I have followed and learnt a great deal from NeHe's tutorials, but I am told half of it is actually 'C' and not 'C++' (char is C right among other problems)
And yeah great, no globals .. code looks good.

Only because half of it is 'Hard Coded', like the problem here with screen Width and Height, he has only got round it, by Hard-coding it 3 times.

[Edited by - dazedandconfused on March 25, 2007 9:48:00 AM]
It's not until we have fallen, we realize how much it hurts !
Quote:Original post by Spoonbender
Quote:In my opinion, there are at least two good reasons to use singletons (and thus yes, global objects) :
1) optimization

Uh?
Singletons are not faster. Globals are not faster either. (True, you save having to push a few bytes onto the stack when calling a function, but in return you lose data locality which will give you cache misses, and the compiler is unable to perform many optimizations on a global because it can be accessed from anywhere, so it has to be written to memory as soon as it's updated. It's no good just holding the new value in a register, because then other parts of the code may just decide to access the obsolete version in memory.
(...)
And if something has to be accessed anywhere in the code, you have a really bad design.
I'd certainly hate for all parts of my program to be able to access a database.

Some people seem to be answering only for the pleasure they must get of flaming others, and they often state things that can be argued. I am not so sure that singletons/globals cannot be optimized, as the /GL option seem to do. I was not paid as an "optimizer" in my company though, but I am sure Spoonbender is.

Usually these guys miss something important : being practical. Being practical means that sometimes one needs to *de-normalize* models so that they work *better*, which for a video game --because we speak about video games here right?, translates into "run faster". Same for the time you spend modeling your project. If I find it easier to use a singleton and I can have something that works and sells two weeks before you, it can be a good decision. That's being practical ; but it seems that many guys who answer these forums do not have yet relevant industry experience...
These guys must think that Quake's source code had a "bad design" and that they would have done much better ! C'mon, it has globals !!
Quote:
I fail to see the connection between lifetime and scope. An objects lifetime may be the same as the program, but that doesn't mean it should be visible to every part of the program.

You might fail to see the connection because you failed to read correctly what I wrote. Never in my post I said such a thing.

And some guys will give the example of the *big* project that they worked on. They will tell you that it was for the NASA or something, telling you that "NO!", they never used a global variable, and that they could actually be publicly whipped for that.
Quote:
Which is actually a bad idea in a large project, where the database server could be split into several servers to split the load. Yes, I've had this happen, and I was happy to have a protective abstraction layer between the server access and the data usage.

Sometimes I feel that if I stated "we should love and cherish each other" in my posts, I would find people answering "WRONG, I have seen situations where you'd better beat the crap out of ...".

Replicon was right. Huge debate for nothing...
StratBoy61
/me puts on moderator hat

Guys, dazedandconfused is around day twelve of "Learn C++ in 21 Days", so to speak, and y'all are arguing about GoF patterns. Take it to another thread. If you're going to post here, post about what he's posting about, not what you feel like posting about.
Thanks Sneftel,

Yeah I'm starting to lose the plot, if you get my drift.

Cheers
It's not until we have fallen, we realize how much it hurts !
Quote:Original post by dazedandconfused
Thanks Sneftel,

Yeah I'm starting to lose the plot, if you get my drift.

Cheers


That's because you have been copying&pasting code from Nehe, building a large project out of it, and now you try to organize it better while you haven't got a strong grasp of OO concepts yet.

It doesn't work that way. You're a beginner, you can't understand a concept AND handle a big,messy project(creating windows,rendering,handling messages,handling input...) at the same time. If we explain to you how to turn function A from global-depended to indepedent, you'll ask how to do it for function B. If we answer for B, you'll ask for C. If we answer for A,B,C, you then ask for the whole project's source. That's because currently you can't see the forest, only the tree. You're looking at a big blob of code and you're lost. You need to figure this out yourself, so you can see what's going on in all situations. Try practicing some more and start from the bottom. Read some articles about OO design, and try to work with small examples so you see how concepts work. I'm afraid you're currently a bit over your head here.
OUCH [depressed]

that's a little harsh, and a pointless post.

While I admit my C/C++ may be a little rusty, I have programmed for some time.
And to be honest LOGIC is LOGIC !

I'm simply asking advice from some "seasoned pro's" on some better techniques.
And to be honest, this is a 'non-profit hobby' project, and globals don't actually bother me.
But I may as well learn to be a better programmer in the process.

I'm not asking for someone to code an entire OpenGL project for me, I can do that myself.
It's not until we have fallen, we realize how much it hurts !

This topic is closed to new replies.

Advertisement