The Anti-Global Variable Front

Started by
5 comments, last by JohnBolton 19 years, 6 months ago
I know alot of people (my software design professor included) have this fervent hate for global variables, because they can cause side effects within functions that reference them, and violate the self-contained nature of modules. Now I am all for tight, self contained and self reliant modular programs, but in some instances I don't really see a way around the global issue. According to the aforementioned professor, anything that needs to be kept handy about environment variables should be passed in as parameters when needed and that way can help keep the function working only with specified inputs and produce only specified outputs. But what about the instance in which a set of functions operates based on the values of a number of state variables? Or, for a more concrete example--in my current project the initialization of the video, audio and input subsystems is dependent on values in the configuration file, which is read into a structure at program startup. This structure is also referenced in the options menu for proper updating prior to a program restart. What would be the alternative to making this structure globally avaiable and still making it accessible to five or six other completely seperate modules?
Advertisement
Use globals when the design calls for globally accessible data. If you need to enforce policy on the data's access and/or use, then wrap the data in a separate compilation unit, and then just expose your control and access functions via a header file. Just use the appropriate tool for the job. Know what you're doing, and it shouldn't bite you in the ass.

Occam's Razor strikes again.
daerid@gmail.com
Le Singleton.

Then again, Singletons Are Evil.
The audio, video, and other subsystems should expose an interface to let you modify their properties. For audio, you could set the ambient sound volume, sound effects volume, etc. For controls, you could provide methods to add and remove control bindings.

On program startup, parse the config file, and get interface pointers to your subsystems. Then set the properties using the loaded values from the config file. Ditto with settings being changed.

Edit: interesting to see the contrast of approaches advised on this issue. I go for the pure OO way, dae goes for globals, Oluseyi goes in-between with singletons.
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
It's true that global structures can be useful sometimes, but you want to work towards having minimal global data. This makes bugs more likely to happen and debugging difficult. If it's something like configuration settings, there's a good case for making it global. Other things can be passed in.

In C++, you can initialize everything by passing in parameters to a class constructor of the class that represents your module.

You appear to be using C or some other procedural language, so your approach might be different. Instead of storing a bunch of data in global variables, you can pass it to the function to use. This is easier for debugging, as you can more easily trace where what gets changed.

Instead of giving it gobs of parameters, put all the data in one or two convenient structures. The configuration, although it could be global, could be put in one structure and then passed as a parameter to any function that needs it. Drawing functions could receive a structure, which contains all the information about the screen, graphics, etc. that the function needs for drawing. A line function, for example, will receive a device context and coordinates as parameters. The coordinates tell it where to draw the line and everything else it needs is contained in the structure.

This, however, presents a problem: there is a lot of copying every time a structure is passed. This can be prevented by creating the structure somewhere and passing a pointer to the structure to every function that needs it. That will prevent inefficiency from copying.

That type of thing is good for data that isn't really global. Things like configuration settings usually apply for the entire application, and are really global data. You could perhaps provide functions that everyone uses to read or write the global data, but in C, it's difficult to prevent anyone from going directly to the data itself.

C++ is a bit nicer for this purpose because you can encapsulate and control access to data when you store it in a class.

I'm writing a game engine right now, and certain things such as the configuration manager and the log manager are available globally. Through encapsulation, however, I can prevent code from changing the configuration in ways I don't want.

I personally think that there are sometimes reasons for having global data, but it should be used very sparingly. It can be a big cause of headaches. I remember when I was first learning to program using QBasic, I didn't know about passing data as parameters, so I used global variables for absolutely everything that was going to be passed from one place to another. It was a terrible nightmare to maintain and debug. It was horrible to try to figure out where the variables were being changed and sometimes some code would stomp on the data of another piece of code. This was especially bad when I was using global variables in different places for different things. *This is a nightmare you want to avoid at all costs!* Any global data should mean the same thing to all the code everywhere in the program, and should be treated the same way by all code. In large projects, where you have different people with different understanding of the code, this becomes very difficult to ensure, and is a good reason why global data should be minimized.
Actually I'm using C++.

As for passing the configuration structure to every function to needs it, or a description of the screen setup to drawing functions; that seems so illogical to me. Writing an initialization function that accepts a config structure says to me that the function will be expected to operate on different configurations within one program instance; however there is in actuality only one configuration, as read from the game config file.

Oh well, my program design usually leans towards C at the lower levels, where (to me at least) class abstraction and all those neato features don't make as much sense, and then working into the engine becomes more classic C++ with a nice class hierarchy and inherently higher level code.

I'm still contemplating doing a depth-first trip through my source tree and straightening out a few frayed wires before moving on, to make things a little neater and acceptable to the outside world.
On one hand you say that the configuration data is for initialization, and on the other you say it is illogical to pass configuration data to drawing functions (which I assume are not part of initialization). Well, both make sense and neither imply a need for global variables.

Here's how I would do it:

1. A "main" module that owns, manages, or simply initializes the video, audio and input subsystems loads the configuration file.
2. The "main" module uses the configuration data to create/initialize the video, audio and input subsystems.
3. The "main" module can destroy the configuration data (or not) since it is no longer needed (unless the "main" module is going to reinitialize something later).
4. Nobody needs to access the configuration data except the "main" module.

... or perhaps like this:

1. Each of the video, audio and input subsystems loads its own configuration data when it is initialized.
2. Nobody outside of the subsystem needs to access its configuration data.

... or perhaps a combination of the two. In any case, there is no need for global data.

John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!

This topic is closed to new replies.

Advertisement