Easy global variable management in C++

Started by
23 comments, last by iMalc 13 years, 7 months ago
Hi there,

I posted a question about global variable declaration and externing in this thread yesterday (you don't need to read it):

http://www.gamedev.net/community/forums/topic.asp?topic_id=580798

Basically, I have been making the transition from standard C to C++ over the past few days and was pulling my hair out over some of the differences between the two languages. Specifically, I was very annoyed at the fact that declaring global variables in C++ is not as simple as it is in C.

In standard C, you can simply declare your global variables in your program's main header (assuming you use one) and leave it at that. Depending on which compiler you use and the parameters you pass to it, C++ handles it in a different manner. In most cases, a C++ compiler will complain when it encounters a variable that has already been declared in a particular scope, which happens if you include a main header in your entry point .cpp file that lists your global variables.

Solving this problem is usually either an annoying case of externing every variable whenever need to use it in a different file or by including a header file in your source code that lists all of the externs to global variables that have been declared in your program. Some compilers can get around this annoying hastle depending on the way they put the program together and the parameters you pass to it, but this is not really necessary as I will demonstrate.

In either case, you need to type more than you should, and in an ideal world, you would only need to declare your global variables once and leave it at that.

After tinkering around for a little while, I figured out a very simple way of doing this:

1) Before you include your main header file in your entry point file, insert this:

#define GLOBAL


From there, you would insert your main header file as usual:

#include "game.hpp"


2) Once you have that define in place, open your main header file or the header file where you declare your global variables (I use one called "data.hpp" which is included in my game's main header, for instance). Insert the following code before you declare your global variables:

#ifndef GLOBAL#define DATA extern#else#undef GLOBAL#undef DATA#define DATA#endif


From there, all you simply need to do is prefix all of your global variable declarations with DATA (or whatever term you want to use). For example:

DATA bool active;DATA string title;DATA Game* game;


And that's it! Now you don't need to manually extern everything, which saves a lot of work.

To explain how it works, basically the compiler will understand that it needs to declare variables as data for your program's entry point file and use them as externs for everything else. To the compiler, the entry point of the application is telling it that DATA represents white space, which is exactly the same as declaring a variable as you normally would. Every other source code file in the program interprets DATA as extern, so your global variables are only declared once.

I hope this is helpful to you guys!
- Sig
Advertisement
Unless I've missed something, you've just gone from prefixing your variables with "extern" to prefixing them with "DATA". One might argue that the former is better since any person looking at the code for the first time will know what "extern" means but be unsure of "DATA".

As a side note, global variables tend to be frowned upon in many cases in "proper" C++.
Humble people don't refer to themselves as humble (W.R.T. "IMHO".)
Quote:Original post by px
Unless I've missed something, you've just gone from prefixing your variables with "extern" to prefixing them with "DATA". One might argue that the former is better since any person looking at the code for the first time will know what "extern" means but be unsure of "DATA".


I'm pretty sure that sticking extern in front of your global variables when they haven't been declared doesn't declare them as global variables, right?

Quote:Original post by px
As a side note, global variables tend to be frowned upon in many cases in "proper" C++.


Shouldn't be a problem if no one else is reading your code :)
// Globals.hstruct Globals{  // all of your globals here  // constructor, if you want.};extern Globals globals;//Globals.cppGlobals globals;
Quote:Original post by Nypyren
// Globals.hstruct Globals{  // all of your globals here  // constructor, if you want.};extern Globals globals;//Globals.cppGlobals globals;


I used this method for a while, to be honest it is rather annoying to prefix everything with globals.
extern declares a global variable. Without extern it's a definition.

In any case, there isn't much point to creating a technique to ease use of master headers. In the long run it's more important to learn to properly organize your headers and source files. C++ compile times are bad enough without inviting spurious recompiles.
I think the real question is, what are you using these globals for? In proper C++ you'd be trying to make everything a bit more object oriented. You'd place most the data you can into some classes that handle said data. You, pass objects around instead of referring to global state. And any global things that you really only need or want one of, you stick into class instances at global scope like Nypyren mentioned.

Using globals is just asking for trouble in the general case.
1) If anyone else uses the code, there is a chance your global state will get read/modified when you didn't expect it. And that is hard to track down.
2) It isn't thread safe, and due to zero encapsulation, point #1 is escalated. Anyone who wants to modify the global state has to know that X variable can only be touched after grabbing mutex Y. Properly encapsulating it into classes lets you create getters/setters/automatic locks/etc to validate that you are properly accessing the data. And, the better choice is to not even have this global state if you are using threads.

Alright, so what is the best method of using this "global" data? Is it ok to simply just declare your data when you need it in a particular scope and leave it at that? Or does having a file full of externs suffice (assuming this is good practice)?
Define your variables in the smallest possible scope (possibly introducing an artificial scope with curly brackets if it makes sense), and declare them only from locations that require their use.

If you follow this rule of thumb, you'll virtually never create a true global, because you'll have all of your data in local or object scopes 99% of the time.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

As SiCrane said you *declare* global data in the header (this is where "extern" comes in), and you *define* global data in the C++ source file. Declarations say "Hey compiler, here's a symbol I'm going to use. Assume its good for now, and resolve it fully later on when I define it."

Two observations:
1) Most generally, reliance on globals is frowned upon in all languages, not just C++. If your application has many globals, then it is most-probably poorly or lazily designed.
2) More pointedly, the fact that you apparently have so many globals (and are likely intent on adding more) to the point that you are seeking out machinery to make it "easier" says to me that you are currently inclined to embrace such poor practices. Whether this is due to ignorance of better ways or stubborness on your part I can't judge, but neither is a valid justification.

This is not meant as a slight against you, as you may simply not know any better or perhaps you recieved poor/undirected instruction. The point I and others are trying to make, however, is that reducing or eliminating your reliance on global state causes the redundancy you complain about to vanish, along with many other benefits.

There are times and places for global state to be sure -- Sometimes because it is "correct" and sometimes because it is impractical to retrofit old code with new, wide-scale features (which is one possible justification out of a handful of "lazy solutions" that are sometimes reasonable to make.)

To give you some idea of how little globals can be used, I believe I've used perhaps 1 in the last 50-100 thousand lines of code I've written (ignoring hackish little throw-away tools, where their simple convenience and small scope of the code -- usually just a couple files -- make the drawbacks virtually non-existant) -- and I later refactored in such a way that even that was no longer global.

throw table_exception("(? ???)? ? ???");

This topic is closed to new replies.

Advertisement