• Advertisement
Sign in to follow this  

Unity Easy global variable management in C++

This topic is 2733 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
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++.

Share this post


Link to post
Share on other sites
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 :)

Share this post


Link to post
Share on other sites

// Globals.h
struct Globals
{
// all of your globals here
// constructor, if you want.
};

extern Globals globals;


//Globals.cpp
Globals globals;

Share this post


Link to post
Share on other sites
Quote:
Original post by Nypyren

// Globals.h
struct Globals
{
// all of your globals here
// constructor, if you want.
};

extern Globals globals;


//Globals.cpp
Globals globals;


I used this method for a while, to be honest it is rather annoying to prefix everything with globals.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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)?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Thanks for your help guys. I'm wondering why declaring values as globals is considered bad practice, though. Does it have an effect on performance or is it more of a good coding practice?

Share this post


Link to post
Share on other sites
I already tried to mention a few reasons above. But off the top of my head:

1) No access control. Anyone, anywhere, can modify or inspect a global. This is bad, as it leads to hard to track down bugs.

2) Not thread safe. Because of #1, it can be hard to be sure everyone is properly locking the data. This makes for even harder to track down bugs.

3) Order of initialization problem. For any non-pod type, it will have a constructor called in the order the object appears in the file. But, between files, there is no standard way to guarentee that foo.cpp's constructors are run before bar.cpp's constructors. So you can't be sure things init in the order you want them to.

4) Not memory friendly. Accessing a global means reading some random memory location. Due to cache locality being a big factor in application speed, you want to keep from accessing random locations. You want to access locations in-order (array) or in recently used order (the stack) or in related to order (by grouping into a class/struct object) to insure it is in the cache. You also need to watch out for false-aliasing, and that is really hard to do with a global, as the global is very likely to randomly evict some piece of data you need from the cache.

5) Not optimization friendly. The smaller the scope in which you define your variables, the better chance the compiler is going to have to optimize the use of the variable. The is especially important considering the compiler may not have a perfect global optimizer, and so most optimizations take place on a single cpp file at a time. In the case of a global, it knows (unless you put static on it) that you could have called extern for it somewhere else in code, and so it can't optimize away the variable.
So, if at global scope you have:

int myNumber = 15;
vs
static int myNumber = 15;

For the first one, the compiler, even if it sees you NEVER called "myNumber = n" in this .cpp file, can't remove myNumber. In the second case, however, if you never said "myNumber = n" then the compiler will treat it the same as
static const int myNumber = 15;
and it will "fold" the 15 into your code, and "myNumber" ceases to exist in the final code.

Share this post


Link to post
Share on other sites
Well if global variables have a performance impact then that's a good enough reason for me not to use them.

Share this post


Link to post
Share on other sites
Gonna play devil's advocate for a moment and give a good use for globals ;P


Used sparingly it will give you access to a classes internal stuctures. Case in point, I have my hud code in one class, my INI parser in another. I tell the hud to create a new hud from the parser with default parameters. I take that hud and parse in each needed variable directly into the hud structure, no middle stucture involved. All I need is the number of the blank hud entry and direct access to the hud data. The advantage is I only fill in the hud data that is actually present in the INI file.

BTW, you can create a common global file for your externs by making a .inc file then including it where needed.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sigvatr
Well if global variables have a performance impact then that's a good enough reason for me not to use them.


That should be the least of your reasons. The impact of globals on the overall architecture and design of your code is far more important than a trivial microsecond here and there. You will waste literally trillions of times more time debugging and trying to understand badly written code than you will lose on slightly-less-than-optimal code, over the course of your lifetime.



Quote:
Original post by LancerSolurus
Used sparingly it will give you access to a classes internal stuctures. Case in point, I have my hud code in one class, my INI parser in another. I tell the hud to create a new hud from the parser with default parameters. I take that hud and parse in each needed variable directly into the hud structure, no middle stucture involved. All I need is the number of the blank hud entry and direct access to the hud data. The advantage is I only fill in the hud data that is actually present in the INI file.


A much superior alternative would be to pass an INI parser instance to the constructor of the HUD wrapper class. This allows you to do things like configure different HUDs based on different INI files (yayy for per-user settings). It also lets you do things like decouple the INI parser from the INI format (just write a "configuration parser" interface and implement that interface from your INI class, XML class, JSON class, whatever) so that you can configure the HUD from different data sources, even including manual input from a debug console or something similar. Neither of those two is nearly as practical when using a global.

Access to a class's internals is bad. You should be designing your code so that you minimize the dependence between unrelated chunks of code, not increase it. Hide details behind interfaces whenever possible - this improves flexibility, increases modularity and reusability, decreases the amount of code that a bug can negatively influence, makes code easier to test and verify, and generally makes your architecture cleaner and simpler to maintain.


Quote:
BTW, you can create a common global file for your externs by making a .inc file then including it where needed.


Uh... what? What does this do that using an .h file in the same role does not?

Share this post


Link to post
Share on other sites
Well for one I tend to write integrated routines, as in routines that function as a small part of the whole. The reason I chose the INI parser is a perfect example of why I chose to use globals. When parsing I do not have the whole section, I get it a single line at a time. So I create a blank entry, then fill in each part as the parser gets to that line. In this case I have no idea what the next line will contain, but as long as it belongs to the same hud section it will get added to the correct hud entry.

As you have said though, the more you can encapsulate your classes the better, it builds for long term reuse. I mainly use it when I build one class onto another and want the two classes to act as one.

If you can do it entirely OOP method that is the best for reusability, but you do sacrifice speed in certain cases.

Share this post


Link to post
Share on other sites
Quote:
Sigvatr
Basically, I have been making the transition from standard C to C++ over the past few days

[...]
Quote:
#define


You can't make a transition from C to C++ in a few days, as above quotes from your OP prove to every halfway experienced C++ programmer.

Not only are global variables frowned upon in C++, but also #define-macros. The latter don't play fine C++, as:

* they don't have scope (they are simply there)
* they can't be used as template arguments in themselves
* they can change meaning of code far away in the compilation unit
* macro expansion can yield sub-performant or even buggy and dangerous code
* not typesafe

In more than 999 ‰ of cases, C++ has better builtin tools (in picogen, which weighs roughly 50kLoC at the moment, I have less than 50LoC of macro code, i.e. less than 1 ‰ overall, hence my claim; and that macro code is only to print out some compiler-id at runtime).

Share this post


Link to post
Share on other sites
Quote:
Original post by LancerSolurus
Well for one I tend to write integrated routines, as in routines that function as a small part of the whole. The reason I chose the INI parser is a perfect example of why I chose to use globals. When parsing I do not have the whole section, I get it a single line at a time. So I create a blank entry, then fill in each part as the parser gets to that line. In this case I have no idea what the next line will contain, but as long as it belongs to the same hud section it will get added to the correct hud entry.


This sounds like a blatant shortcoming of your INI parser; why not just load the whole file at once, and parse it into sections of key/value pairs? Could be done with a std::map<std::string, std::map<std::string, std::string> > trivially, with a templated wrapper function for typesafe conversion from the internal value string to the desired type using lexical_cast or stringstreams.

In any case, I really honestly fail to see how this adds up to an argument for global variables. If anything, it's a classic demonstration of how reliance on globals has crippled your code feature-wise and made it harder to maintain!


Quote:
As you have said though, the more you can encapsulate your classes the better, it builds for long term reuse.


I said interfaces, not classes. My recommendations hold for functional, procedural, or even declarative programs just as much as for object-oriented ones.


Quote:
I mainly use it when I build one class onto another and want the two classes to act as one.


Not sure what you mean by this, but it sounds fishy to me. Why do you have two classes that you want to treat as a single class? Why aren't you using inheritance, composition, or simply refactoring into a single class here?


Quote:
If you can do it entirely OOP method that is the best for reusability, but you do sacrifice speed in certain cases.


Uh, no.

Please, show me some profiling where well-written OO code performs notably worse than the equivalent procedural/functional/otherwise-non-OO code.

This is superstition at best, and outright misinformation at worst.

Share this post


Link to post
Share on other sites
Quote:

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.

C++ and C use the same rules for global variables. Any difference you are seeing stems from your misunderstanding of how C's compiling and linking stages actually work. It is possible that the code appears to be working how you described, but unless you are externing in the header file and defining in a single source file you are doing it wrong.

Share this post


Link to post
Share on other sites
Quote:

... the more you can encapsulate your classes the better, it builds for long term reuse.

Your classes are coupled, not encapsulated. Coupling actually makes it harder to re-use code.

Quote:

If you can do it entirely OOP method that is the best for reusability, but you do sacrifice speed in certain cases.

I hope your INI parser isn't designed in that manner as an "optimisation". The bottleneck in such code is certainly going to be the disk access, not what happens next.

Share this post


Link to post
Share on other sites
Quote:
Original post by rip-off
C++ and C use the same rules for global variables.

Not really. C contains some frankly bizarre rules regarding globals. One of which that the OP seems to be relying on is the concept of tentative definitions. In C the following is legal at global scope:

int i;
int i;

This creates one definition for i. This is further complicated by the fact that some compilers, for example some versions of gcc, place globals with only tentative definitions into the common segment of object files, which causes all but one to be discarded at link time. The closest C++ analog to this behavior is a static function variable in an inline function. AFAICT C's linkage rules for globals are supposed to be the same as C++'s but I'm not as familiar with the C standard as I am the C++ standard.

Share this post


Link to post
Share on other sites
I really don't see what is the point of this technique. Declare the variable in one header, make sure it says extern, and in the cpp file actually define it (i.e. without extern).

Share this post


Link to post
Share on other sites
Quote:

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


Other than global state being evil in a variety of ways, naming globals with short, cute names a bad idea. Not only is "active" an incredibly vague variable name, it becomes increasingly so when you put it into global state without any context at all as to how it is used. With title, WHAT is the title referring to? I understand "game" is probably your game state, but it isn't exactly clear what that is.

A good rule to follow is that the larger the scope of a variable the more descriptive its name should be. It's probably even worth it to have a prefix for global variables simply because they should be rare and you really do want to underscore when you are manipulating global state.

Don't be afraid to write things like: g_GameTimerIsActive, g_OperatingSystemWindowTitle, g_GameStateManager

Remember, it doesn't cost you anything to write out a few extra words to describe the variable. You don't pay per character or anything. Be descriptive.

With that said, NONE of these things are a good candidate for a global variable.


[Edited by - M2tM on August 30, 2010 7:44:31 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Quote:
Original post by rip-off
C++ and C use the same rules for global variables.

Not really. C contains some frankly bizarre rules regarding globals. One of which that the OP seems to be relying on is the concept of tentative definitions. In C the following is legal at global scope:

int i;
int i;

This creates one definition for i. This is further complicated by the fact that some compilers, for example some versions of gcc, place globals with only tentative definitions into the common segment of object files, which causes all but one to be discarded at link time. The closest C++ analog to this behavior is a static function variable in an inline function. AFAICT C's linkage rules for globals are supposed to be the same as C++'s but I'm not as familiar with the C standard as I am the C++ standard.

Very interesting. Though not entirely surprising, C is a bit of a minefield of weird legacy stuff. Maybe it would have been better to say that "idiomatic" C uses the same rules, but C is often the home of extremely idiosyncratic styles.

Share this post


Link to post
Share on other sites
I learnt of this exact trick 12-13 years ago. I think I used it in one Uni assignment but that's it.
It's not a real-world useful thing as globals are mostly very rare, and it doesn't save typing, it just confuses others.

It also encourages you to put variables in header files that might best only be global within that cpp file, i.e. preferably in an unnamed namespace or static. Thus now they are exposed to a lot more of your program which makes tracking down where they're used a bit harder.

Overall, it solves the wrong problem.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By Florencia Sylvester Anelli
      Hi everyone! I've developed a game called The Last Dog (ZOMBIES + DOGS) and was wondering if you could give it a try in order for me to analyse your behaviour in analytics. Give it a shot! Please download it here: https://florsyl.wixsite.com/thelastdog2 and let me know if you detect any bugs. VIDEO.3gp
       

       
       
    • By Manuel Berger
      Hello fellow devs!
      Once again I started working on an 2D adventure game and right now I'm doing the character-movement/animation. I'm not a big math guy and I was happy about my solution, but soon I realized that it's flawed.
      My player has 5 walking-animations, mirrored for the left side: up, upright, right, downright, down. With the atan2 function I get the angle between player and destination. To get an index from 0 to 4, I divide PI by 5 and see how many times it goes into the player-destination angle.

      In Pseudo-Code:
      angle = atan2(destination.x - player.x, destination.y - player.y) //swapped y and x to get mirrored angle around the y axis
      index = (int) (angle / (PI / 5));
      PlayAnimation(index); //0 = up, 1 = up_right, 2 = right, 3 = down_right, 4 = down

      Besides the fact that when angle is equal to PI it produces an index of 5, this works like a charm. Or at least I thought so at first. When I tested it, I realized that the up and down animation is playing more often than the others, which is pretty logical, since they have double the angle.

      What I'm trying to achieve is something like this, but with equal angles, so that up and down has the same range as all other directions.

      I can't get my head around it. Any suggestions? Is the whole approach doomed?

      Thank you in advance for any input!
       
    • By devbyskc
      Hi Everyone,
      Like most here, I'm a newbie but have been dabbling with game development for a few years. I am currently working full-time overseas and learning the craft in my spare time. It's been a long but highly rewarding adventure. Much of my time has been spent working through tutorials. In all of them, as well as my own attempts at development, I used the audio files supplied by the tutorial author, or obtained from one of the numerous sites online. I am working solo, and will be for a while, so I don't want to get too wrapped up with any one skill set. Regarding audio, the files I've found and used are good for what I was doing at the time. However I would now like to try my hand at customizing the audio more. My game engine of choice is Unity and it has an audio mixer built in that I have experimented with following their tutorials. I have obtained a great book called Game Audio Development with Unity 5.x that I am working through. Half way through the book it introduces using FMOD to supplement the Unity Audio Mixer. Later in the book, the author introduces Reaper (a very popular DAW) as an external program to compose and mix music to be integrated with Unity. I did some research on DAWs and quickly became overwhelmed. Much of what I found was geared toward professional sound engineers and sound designers. I am in no way trying or even thinking about getting to that level. All I want to be able to do is take a music file, and tweak it some to get the sound I want for my game. I've played with Audacity as well, but it didn't seem to fit the bill. So that is why I am looking at a better quality DAW. Since being solo, I am also under a budget contraint. So of all the DAW software out there, I am considering Reaper or Presonus Studio One due to their pricing. My question is, is investing the time to learn about using a DAW to tweak a sound file worth it? Are there any solo developers currently using a DAW as part of their overall workflow? If so, which one? I've also come across Fabric which is a Unity plug-in that enhances the built-in audio mixer. Would that be a better alternative?
      I know this is long, and maybe I haven't communicated well in trying to be brief. But any advice from the gurus/vets would be greatly appreciated. I've leaned so much and had a lot of fun in the process. BTW, I am also a senior citizen (I cut my programming teeth back using punch cards and Structured Basic when it first came out). If anyone needs more clarification of what I am trying to accomplish please let me know.  Thanks in advance for any assistance/advice.
    • By Yosef BenSadon
      Hi , I was considering this start up http://adshir.com/, for investment and i would like a little bit of feedback on what the developers community think about the technology.
      So far what they have is a demo that runs in real time on a Tablet at over 60FPS, it runs locally on the  integrated GPU of the i7 . They have a 20 000 triangles  dinosaur that looks impressive,  better than anything i saw on a mobile device, with reflections and shadows looking very close to what they would look in the real world. They achieved this thanks to a  new algorithm of a rendering technique called Path tracing/Ray tracing, that  is very demanding and so far it is done mostly for static images.
      From what i checked around there is no real option for real time ray tracing (60 FPS on consumer devices). There was imagination technologies that were supposed to release a chip that supports real time ray tracing, but i did not found they had a product in the market or even if the technology is finished as their last demo  i found was with a PC.  The other one is OTOY with their brigade engine that is still not released and if i understand well is more a cloud solution than in hardware solution .
      Would there  be a sizable  interest in the developers community in having such a product as a plug-in for existing game engines?  How important  is Ray tracing to the  future of high end real time graphics?
    • By bryandalo
      Good day,

      I just wanted to share our casual game that is available for android.

      Description: Fight your way from the ravenous plant monster for survival through flips. The rules are simple, drag and release your phone screen. Improve your skills and show it to your friends with the games quirky ranks. Select an array of characters using the orb you acquire throughout the game.

      Download: https://play.google.com/store/apps/details?id=com.HellmodeGames.FlipEscape&hl=en
       
      Trailer: 
       
  • Advertisement