Skip to main content
GameDev.net gamedev.net
🔒 Locked

Global variable?

Started by nuclear123 Oct 24, 2011 at 12:24 PM 9 replies 1.7k views
Original Post
nuclear123
nuclear123
What signifies or usually causes you tocreate a global variable in a program?
thebigbossman
thebigbossman

bad code


While I agree, in principle (and it is easy to be theoritical)...

In actual coding practice however, there are many situations where the most elegant and readable solution is probably using a global variable or object. Sometimes having to pass around a lot of data in functions belonging to different classes it just makes more sense to store certain program-wide information not belonging to any particular class (such situations do arise!) in globals.

They should be minimized, but they exist for a purpose and that purpose may arise in some situations. Not saying it is recommended as a general practice. But it has its uses.
Telastyn
Telastyn
I'll define globals as constants, helper methods, state for the occasional helper method, and rarely some sane default for when I don't specify something in a given module. Configuration info can sometimes fall here.

Just like any other structure, I'll use them when it makes my code better. When even accounting for the coupling, debug-ability, maintainability, and concurrency problems caused by globals don't apply enough to make up for the benefits of simplicity and ubiquity.
L. Spiro
L. Spiro
I am afraid that I must agree with Hodgman.

However that is concerning the strictest definition of a global. A global, as just a global, simply will never exist in my code.

However static class members and static local variables are globals in the end, and I use them for:
#1: Data/look-up tables.
-> View the CFrustum::GetNeighbors() function.
#2: Initializers for default values.
#3: Counters for “object’s unique ID”. That is, every texture will have a unique ID, and of course that means a static class member (private or protected) that counts up once each time a CTextureBase is instanciated.
#4: The default memory manager instance. Every memory manager is an instance. The default one to which overloaded new/delete redirect just happens to be a static class member.


A global that is just a global, sitting in the middle of nowhere with free access by anything that includes the file?
Bad design. Static class members may be globals to the compiler, but they have access rights. I don’t want other classes to be able to change my CTextureBase unique ID counter, and I don’t want to hide the unique ID counter in the .CPP file.
#1: What if my constructor is inlined into the header file?
#2: And frankly I simply refuse to declare some variables in the header and some in the .CPP. I want my class variables to be declared in one specific location. It just makes sense.

There is no situation in which a static class member is less elegant/organized than a standard global.


L. Spiro
I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid
way2lazy2care
way2lazy2care

While I agree, in principle (and it is easy to be theoritical)...

In actual coding practice however, there are many situations where the most elegant and readable solution is probably using a global variable or object. Sometimes having to pass around a lot of data in functions belonging to different classes it just makes more sense to store certain program-wide information not belonging to any particular class (such situations do arise!) in globals.


I disagree with your second paragraph. There are situations where using a global variable/object is more elegant and readable than your current solutions, but I really can't think of any scenario where it is THE MOST elegant solution. It will almost always be more readable, but that comes at a huge cost of completely random behavior, which is far far worse than readability problems. If you look deeper there will almost always be a better solution to the problem.

I'm going to link this because the problems are pretty much the same (singletons vs globals) and I posted it this morning:
http://www.gamedev.n...ost__p__4876295

edit: although I do believe sometimes in practice globals can be a much faster solution, which might fit your particular needs at the moment and that in some code bases they make sense, but that is usually because those code bases are usually pretty terrible to begin with.
Telastyn
Telastyn

but that comes at a huge cost of completely random behavior, which is far far worse than readability problems.


By 'completely random behavior', I'm guessing you mean static initialization issues in C++?

Even in that environment, you only run into issues if you depend on order of static initialization (which is dicey at best). I don't want to be the global variable advocate; they have many downsides, but randomness isn't something inherent to their nature.
Serapth
Serapth
I think you need to be careful in your definition. A global mutable variable is much worse than a global const or enum.

I agree with vharishankar, in some cases, if a class or interface is required globally and it is truly a globally required variable, it makes sense for it to be global. In this case however, you generally will surround it in an access layer of some sort. That said, if I was working on a calendaring application and I needed for some reason to define a days of the week enum, I would have no issues with making this a globally available define. Additionally if I had a const property that needed to be accessed throughout the code, I would have no issue with making this globally available either. Neither scenario runs into the majority of the pitfalls associated with global variable usage.
Serapth
Serapth

[quote name='way2lazy2care' timestamp='1319476174' post='4876359']
but that comes at a huge cost of completely random behavior, which is far far worse than readability problems.


By 'completely random behavior', I'm guessing you mean static initialization issues in C++?

Even in that environment, you only run into issues if you depend on order of static initialization (which is dicey at best). I don't want to be the global variable advocate; they have many downsides, but randomness isn't something inherent to their nature.
[/quote]

I previously poo-pooed on C++ for this "undefined behavior" when it comes to static initialization order... until I looked up C#'s details and saw it documented as "implementation specific".

unsure.gif
way2lazy2care
way2lazy2care

By 'completely random behavior', I'm guessing you mean static initialization issues in C++?

Even in that environment, you only run into issues if you depend on order of static initialization (which is dicey at best). I don't want to be the global variable advocate; they have many downsides, but randomness isn't something inherent to their nature.


I mean more to do with problems related to having global alterable state usually due to concurrency.
samoth
samoth
As always it depends. There is nothing inherently wrong with a global (or singleton) as such, as long as there is a good reason for it and as long as it's done with a little thought. And of course, as long as there are not dozens of them all over.

One obvious reason might be that you just need something "globally" or nearly so. Your resource cache, your text/localization manager (or whatever you call it) and your log facility might be examples for that. If those are not "global" in some way, you can neither display an error to the user, nor log an error if something goes wrong. If three subsystems need to load resources from disk and you don't want every subsystem to tamper with the disk on its own behalf, you somehow need to give each of them access to the resource cache too.

The "need everywhere" or "need in multiple places" constraint leaves you with pretty much two choices:
a) Generate an instance in main() and pass a pointer or reference down to every single function (or object) that needs a reference. This avoids a global, but is not really "better". It is the exact same thing except the global is now on the stack, and there are possibly several copies of it. Plus, you must think ahead. If main calls A, A calls B, B calls C and D, and you later add E (which D calls), you might suddenly realize that since E depends on g_X, you must change the signatures of A, B, C, and D to pass a reference. If this breaks the code of 5 other people in your team, you better had a good excuse.
b) Use a global or a global in disguise. Oh my, what a bad style. Right, but has the functionality that is needed.
c) Ok, ok, I said "two choices" Anyway, the third is to use a static or a singleton. This is really b) in disguise.

If you program under Windows, you have a similar situation. To register a window class, you need to pass the current instance. The instance is a stupid, useless magical value passed to WinMain that is good for nothing, but you need it or you can't create a window. Passing NULL won't do.
This is no problem if you register your window class(es) inside WinMain. Now you might want to do this in a different place. Which again leaves only the options of using a global, or passing the value down to the initialization function. In this case, passing the value would admittedly often be the best solution (since it's usually just one function). Then again, you might need that value elsewhere too. Some third party lib might want to create windows too and might ask you in some other place.
Or, you can rely on totally undefined behaviour: Incidentially, the instance is (in current Windows versions) identical to the result of GetModuleHandle(0), and incidentially this is always 0x400000. So if you don't mind that your code might possibly blow up on a future version of Windows, you could just use one of these instead. Now call me a fool, but a global variable looks like not the worst possible solution to me.

Static initialization is no problem, by the way. The behaviour is well defined if you just only create a single global struct containing any other global objects (or pointers to them) you might need. The order of initialization of a single object is well-defined, and a simple struct wrapper is completely optimized out by the compiler.
Serapth
Serapth

Static initialization is no problem, by the way. The behaviour is well defined if you just only create a single global struct containing any other global objects (or pointers to them) you might need. The order of initialization of a single object is well-defined, and a simple struct wrapper is completely optimized out by the compiler.


Just be careful never to initialize one static from another static.

For ( a contrived) example:

static int i = 42;
static int j = i + 1;

That, could get you into trouble.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.