Jump to content

  • Log In with Google      Sign In   
  • Create Account

Calling all IT Pros from Canada and Australia.. we need your help! Support our site by taking a quick sponsored surveyand win a chance at a $50 Amazon gift card. Click here to get started!


Globals


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
13 replies to this topic

#1 maxest   Members   -  Reputation: 442

Like
0Likes
Like

Posted 05 April 2014 - 10:53 AM

Some time ago I worked on a (professional) project in which I was surprised to see quite a lot of globals. There were classes like CRender, CPhysics, CClient and so on, and there was file globals.h that tied together all these classes by declaring GRender, GPhysics, GClient. Now, the entire code relied upon these globals and often even a small change in gpu texture class header caused recompilation of a big part of the codebase (which was medium in size but the total recompilation time was around 1 min).

 

We've heard all these stories about globals being bad and personally I prefer to write code a different way but I must say that working with all these globals was very easy - where ever I was in the code I could always easily access the client's, render's and so on, properties.

 

So, what's your opinion?



Sponsor:

#2 ProtectedMode   Members   -  Reputation: 1335

Like
3Likes
Like

Posted 05 April 2014 - 11:17 AM

Instead of bare globals, I prefer the service locator pattern. This prevents recompiling a lot when making changes. But I personally prefer passing the objects via parameters, or on construction. Or, when for example all deratives of the class object need access to some object, create a protected method getSomeObject(). Simply using globals is not easy to maintain as you said, so I should avoid them where possible.

 

I agree however that easy access via for example globals can be nice to program with, that's why I sometimes use a service locator where it isn't necessary, but only for small projects.



#3 maxest   Members   -  Reputation: 442

Like
0Likes
Like

Posted 05 April 2014 - 05:43 PM

Didn't know the locator pattern before. Sounds like a nice idea although... in itself it needs a global variable :). Or be implemented as a singleton. But hey, can't we just implement CRender, CClient and CPhysics as singletons in the first place? Instead of declaring them global why not just have them declared in their respective files?



#4 frob   Moderators   -  Reputation: 31257

Like
8Likes
Like

Posted 05 April 2014 - 08:45 PM

Global variables represent an implicit hidden dependency.

As such they add complexity everywhere you use them. Do it poorly at your own peril.

Didn't know the locator pattern before. Sounds like a nice idea although... in itself it needs a global variable :). Or be implemented as a singleton. But hey, can't we just implement CRender, CClient and CPhysics as singletons in the first place? Instead of declaring them global why not just have them declared in their respective files?

A singleton means "There can only be one, ever, and it is absolutely mandated and enforced".

A global variable means a shared, well-known instance, but you can still create more if you want.

Don't confuse the two.


Singleton is an anti pattern. Don't do it. Generally don't talk about it except to tell people not to do it. Someone mentions "singleton" and a massive flame war erupts about if they are pure evil, a minor evil, and if maybe some case might be an acceptable evil. Lets not do that.

For your examples, a singleton renderer breaks all kinds of visual effects, breaks several kinds of features like potentially picture-in-picture or certain multi-pass effects or cases where you want a second rendering for your own purposes. A singleton client means you cannot have a second client for any reason, not even as an AI player or a network player or as a stand-in for testing. A physics singleton means you cannot run a separate private physics simulation on any tiny thing for any reason.

When you write a singleton you are almost certainly doing something wrong.

Usually you just want a well-known instance.

Logging is oft-cited as a candidate for a singleton; it should instead be a well-known instance since making it a singleton means I cannot create my own private logging functionality for a class or object instance. Application variables like command line parameters and directory paths and such is sometimes cited as a singleton, but that prohibits me from making my own version for any other purpose, such as custom tools that use the engine, so a well-known instance is preferred. Even a "service locator" system that finds the well-known instance should not be a singleton since it limits me from making my own if I need it; use a global variable, not a singleton, so I can create another if I need it.

If a game is carefully designed and rules are followed, you can have global objects that work okay. By themselves they are not necessarily fatal to the implementation.

If you do decide to share things through globals, you must be absolutely strict about what systems can modify it, when they can modify it, and about what systems can read from it an when reading is prohibited.

Failure to enforce strict controls on global variables leads to all kinds of incredibly nasty bugs, which is why they are called "evil" and should generally be avoided.

Edited by frob, 05 April 2014 - 09:43 PM.
fix formatting.

Check out my book, Game Development with Unity, aimed at beginners who want to build fun games fast.

Also check out my personal website at bryanwagstaff.com, where I occasionally write about assorted stuff.


#5 phil_t   Crossbones+   -  Reputation: 5803

Like
1Likes
Like

Posted 05 April 2014 - 10:31 PM

Almost every time I am lazy and make something a global, I end up eventually needing to make it non-global.

 

I'm also not a big fan of the service locator pattern, though I do use it in places. It makes it hard to understand the dependencies, and you can only have one of a particular service.

 

Best to just declare/pass down dependencies explicitly in most cases.



#6 Álvaro   Crossbones+   -  Reputation: 16524

Like
7Likes
Like

Posted 06 April 2014 - 06:25 AM

I can think of two problems with using globals that haven't been mentioned:
(1) Using globals makes it hard to reason about your code. If you want to make some change to a global object, you may end up having no idea how many parts of the code will need to know about the change. If the change is a change in semantics, even if a small one, it could be hard to convince yourself that it won't break anything, so now you have to test it, and chances are your tests will miss something.
(2) Using globals makes it hard to reuse some part of the code in another project. You never bothered to define a clean interface for your Physics module, so now if you want to use it somewhere else, you may have to spend a lot of time disentangling it from other parts of the code that it really didn't have any business using (the rendering system because there was a ray-scene intersection facility there, or the sound system in response to things hitting each other...), or you'll end up importing the whole code to the new project, even if it doesn't really need to render anything or make any sounds.

I fight with (1) at work. Our most important program has one global god object with a fairly generic name (something like `Game' if it were a video game). Any part of the code can call a global function to get the one instance of the god object. It is not a singleton, but in practice it is impossible to have more than one of these monsters because the code assumes a single instance in so many places (static local variables in some methods, using other global objects...). There are parts of the code that are so complicated that nobody really knows how they work or what they would do in particular scenarios, but nobody dares change them, because you would have to think about how every other part of the code would be affected by your changes, and we are talking about millions of lines of code.

I had a serious case of (2) with my chess program in the 90s and it was a horrible experience, but I learned to value having clear interfaces between parts of the code. Ideally you can take parts of your code and treat them as libraries, with well-defined APIs, some documentation, and maybe even their own versioning. Even if you never end up using one of these libraries in another project, the one project where you do use it will end up being easier to work with because you defined clear interfaces.

#7 Hodgman   Moderators   -  Reputation: 42112

Like
3Likes
Like

Posted 06 April 2014 - 08:30 AM

Didn't know the locator pattern before. Sounds like a nice idea although... in itself it needs a global variable :). Or be implemented as a singleton.

Or it can be not-evil and be neither of those.
If it's needed from program startup until shutdown, then make it a local variable inside main.

Globals are fine if you're writing a simple example, or if you really don't care at all about the long-term maintainability I the code. If the code is being rushed together to ship some product, and then will be thrown away, never to be touched again, then hacky globals are fine.
Otherwise, decades of software engineering practice and study has produced whole books on how to write software that is actually reusable and maintainable, and they all involve some kind of compartmentalization, or: not using globals! :P

#8 Aardvajk   Crossbones+   -  Reputation: 9192

Like
3Likes
Like

Posted 06 April 2014 - 09:46 AM


I must say that working with all these globals was very easy

 

That's kind of the problem in a nutshell though. Its so easy, you end up doing in all sorts of places that later on you'll wish you hadn't.

 

I like the fact that its hard to access my physics code from within my renderer, because it stops me cutting corners that will bite me later on.



#9 Ohforf sake   Members   -  Reputation: 2007

Like
1Likes
Like

Posted 06 April 2014 - 10:06 AM

Multithreading with globals is also a very dangerous thing.

 

If all your data resides in isolated blobbs of object instances, you can be very sure that you can churn through each blobb in a seperate thread without any risk of race conditions. But if you use globals, you have that single data point that is accessed by all blobbs which prevents you from easily parallizing stuff.



#10 NewVoxel   Members   -  Reputation: 387

Like
0Likes
Like

Posted 06 April 2014 - 05:37 PM

I don't think the ease of access is worth the messy code you'll wind up with when the project gets rolling. Granted it can be a pain when you have to go back and edit your constructors to pass in variables you forgot about earlier, but things make so much more sense when only having access to what you need.


Your authority is not recognized in Fort Kick-ass http://www.newvoxel.com


#11 maxest   Members   -  Reputation: 442

Like
0Likes
Like

Posted 16 April 2014 - 12:38 PM

Recently a nice article showed up, http://www.gamedev.net/page/resources/_/technical/game-programming/a-long-awaited-check-of-unreal-engine-4-r3635 where there are parts of Unreal Engine revealed. And what got my attention was this line in one of the listings:

ENGINE_API UEngine* GEngine = NULL;

It looks like even best of the bests use globals... :)



#12 frob   Moderators   -  Reputation: 31257

Like
4Likes
Like

Posted 16 April 2014 - 01:10 PM

Note some details about that global.

First off, the global is invariant. No system within the game modifies it. This avoids the largest collections of issues with global variables. You don't worry about multithreading concerns and race conditions with the value changing, or the value changing mid-use, or locking semantics, or a large number of bugs.

Second, the primary purpose is to serve as a service locator style object. You can use GEngine to find the other components and systems. It becomes a well-known instance, rather than a singleton.

Third (and flowing from the earlier two) the global is not shared state. It does not represent a value or collection of values that many systems used in a free-for-all. The object allows access to subsystems through access control and uses constraint checking, and does so on a well-established term.

Fourth, this is a global pointer, not what most people consider a global object. When most people think of "global" they think of an instance that is initialized statically (before main is called) and cleaned up sometime after main exits. This is a pointer to an object with a well-established lifetime. In that respect it is not what most people think of as a global.

As such it avoids most (but not all) of the problems associated with global objects.


That being said, it still does cause some issues. Systems rely on that value to be set externally and for it to be valid. This can make automated systems like tests harder to implement and maintain. Systems that rely on it have some added coupling that they otherwise might not desire. At a lower level individual components do not need to rely on the object, they can be written in an agnostic way that takes a pointer to a system, with the caller providing GEngine->getWhatever() to provide the specific instance to operate on.

So it is a less-bad situation than it might first appear. It does introduce some avoidable coupling, but the engine designers have minimized the overall problems, and are willing to live with the remaining consequences to take advantage of some convenience it offers.

Check out my book, Game Development with Unity, aimed at beginners who want to build fun games fast.

Also check out my personal website at bryanwagstaff.com, where I occasionally write about assorted stuff.


#13 Hodgman   Moderators   -  Reputation: 42112

Like
5Likes
Like

Posted 18 April 2014 - 01:22 AM

I haven't used Unreal since 2.5, but their code used to be "typical C++ bullshit", incorrect-OO everywhere bad code ;P

Just because a building is popular, or had a lot of tenants, it doesn't mean that the foundations are sound or that renovations would be easy ;)

#14 Stainless   Members   -  Reputation: 1535

Like
1Likes
Like

Posted 18 April 2014 - 01:47 AM

Globals are like any other construct that exists in programming. Used well, they are excellent. Used badly, they are the spawn of satan.

 

I worked for a large company that refused to let developers use global variables, at the time we had 256K of rom for the entire code and 512K of ram.

 

We would develop a product, ship it, archive the source code, then start on the next product.

 

Just before shiping we would always have the problem that we were a few K over budget on the rom and there would be a mad panic to optimise everything.

 

Once I added a single global variable and saved 20K of rom.

 

For me it's not an issue of should I use globals or not, I will if I need to. It's more a case of do you know enough about the system to know when using a global is safe and an improvement.






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS