Where do you keep global variables?

Started by
34 comments, last by Bob Janova 17 years, 6 months ago
I don't see why you would want to use a global anywhere in general, and this includes singletons and other ways to hide the global nature of the data. I'd say that it's best to keep as much of your program referentially transparent as possible. Yes, there will be input and output in some functions, but don't let the I/O "infect" the rest of the program.

This also explains why many (including myself) considers logging to be one of the few valid global areas. The logging does not (or at least should not) affect the referential transparency of any function.
Advertisement
Maybe I should clarify a litte, I don't mean referential transparency(RT) as in "functional programming", even though I like as much RT as possible. The kind of RT I'd at the very least would like to see in C++ is that f(x, y) doesn't affect anything else than x and y. This isn't exactly RT, but it's a lot better than the situation with global variables where it's impossible to tell if a function affects them without actually inspecting it.

Inserting logging into f(x, y) doesn't matter, it could be removed without changing the functionality of the program. I'm not sure what this kind of "weak" RT is called, but I suppose it might have a name too.
sorry if Scene + camera makes u uncomfortable use A + B (though in real world situations invloving ppl + camera often the person wants to know where the camera is, eg a newsreader on tv)
now we have A wanting to view something in B
what code would i use?
I think once the number of params gets to a certain point, you just put them all in a struct - especially if you pass the same params around (down the chain, and to a bunch of sibling objects).

Specifically, I think you can actually get away with passing an interface around that encapulates the camera position etc. eg:

Engine.cpp:  foreach(renderer)     renderer->Render(this);QuakeBSPModel.cpp:class QuakeBSPModelRenderer : public Renderer{...   void Render(RenderEngine *engine)   {      ViewVolume view = engine->GetViewVolume();      // build list of stuff that needs renderer ...      engine->DrawPolygonList(polygons,textures,...);   }}


Here I would make the QuakeBSPModel intimate with the Engine - ie, structured to efficently match the engine's data structures.

So the game tells the engine where to look, and the model renderes itself from data given to it by the engine.
over the years I developed my own semantics in writing globals it goes something like this
// .h filesclass cBasicGlobal{  public:  //conversions  string toString(int c);  string toStringf(float c);  int toInt(string s);  float toFloat(string s);   bool HexToDecimal (char* HexNumber, int& Number);  void IntToHexString(int val, char *lo, char *hi);  //debug  virtual string AppendLog(char *format, ...);  void StartAppend(int indent, string title);  void StopAppend(int indent);  void ResetLog(void);  void SetDebugFile(string path);  void IncInd(int i);  void DecInd(int i);  void SetDebugMode(unsigned char mode);  private:  FILE *log;  unsigned long LogCounter;  string path_;  int indent_;                string indent_string_;      unsigned char debug_mode;};class cGlobal : public cBasicGlobal{ public: cGlobal(); ~cGlobal();  // here goes any global function that   // i will use in current program};extern cGlobal *global;  // define as extern// .cpp file#include "cGlobal.h"cGlobal *global;...

and after that I just allocate global in main func and deallocate before exit.
well this is pretty much how I use it.
Just to jump on the singleton thread-jack again for a second: there is one thing for which there is guaranteed only to be one of them needed in your application: the application! Or, in the case of a game, probably the game engine. Thus something like this (pseudo) is fair enough, and gets you all the advantages of the singleton idea without keeping you to one copy if you find you need more:

main(){ Engine mainEngine(...); mainEngine.run(); // main game loop}class Engine { private:  Renderer* renderer;  SoundSystem* soundsystem;  ... public:  Renderer* getRenderer();  ...}


Then you don't need to 'pass down' anything apart from an Engine& that is the currently running engine; to get at the 'singletons' you just do
engine.getRenderer().whatever(...)


If you later find you need more than one sound system, you can redefine 'getSoundSystem()' to return the currently active one (so most of your code keeps working fine) and have an overload that returns a specific one if a cetain sound needs to go through the neighbours' subwoofer or whatever.

This topic is closed to new replies.

Advertisement