• Advertisement
Sign in to follow this  

Advice on global variables

This topic is 3981 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, Reading several threads and other resouorces, I keep hearing global variables are a bad thing. Is this correct ? and what is the best solution to avoid using globals? Here is a quick example from a project, I'm not looking for anyone to re-code this, just advice on better programming techniques.
// Window Enviroment Settings ----------------------------
HDC		hDC = NULL;		
HGLRC		hRC = NULL;		
HWND		hWnd = NULL;		
HINSTANCE       hInstance;		
DEVMODE		DMsaved;		

bool		keys[256];		
bool		active = TRUE;		

bool		fullscreen = FALSE;	
bool		scrVSync = FALSE;	
int		scrWidth = 1024;	
int		scrHeight = 768;	
int		scrBitDepth = 32;	

// Camera
CCamera		objCamera; 
float		CAMERA_FARPLANE = 50000;	
float		FOV = 52.0f;		

// Font & Text variables
GLuint		baseFont;
GLuint		baseBitmapFont;
GLuint		texBitmapFont;

// Textures
GLuint		texCrosshair;
GLuint		texHud;

// Mouse Variables
// Move & click
bool		MouseMove = FALSE;    
int		MouseButton = -1;	
//Mouse Position
GLfloat		MoveXcoord, MoveYcoord;		
int		clickXcoord, clickYcoord;

// timing Variables
int		FPSCount = 0;
char*		FPSCountTxt = "";
DWORD		timeCurrentTime = 0;
DWORD		timeDemoStartTime = 0;
DWORD		timeElapsedTime = 0;
char*		timeElapsedTxt = "";

// Testing Variables
bool		showHelp = FALSE;
bool		showWireframe = FALSE;
bool		showFog = FALSE;
bool		enableLighting = TRUE;
bool		enableSound = TRUE;
bool		enableFrustum = TRUE;




Already this is getting very large, and very messy, and this is still a simple project. Most of the examples I have been following are from NeHe, which I'm told some of his code is actually C, not C++ !! Although the 'code downloads' for the lessons are labelled as Visual C++ (sorry noobie alert) I'm still learning the difference. Besides the point anyway, but is there a better way to structure an application, and avoid so many globals ?? Thanks

Share this post


Link to post
Share on other sites
Advertisement
When people tell you something is bad or wrong (programming-wise), they usually mean that you shouldn't use it unless the alternative is worse. For instance, many sources will tell you that goto should never be used. However, there are times when it can be useful.

I would suggest trying to avoid using global variables as much as possible. There's very little security regarding when and how these variables can be altered. This can make debugging and maintenance much more difficult.

Personally, I would try to wrap some of the values up into a class or struct, just for the sake of readability and maintenance. All the screen stuff can go into a class. The camera, font and mouse variables can also be placed into their own classes. At program start up, just create instances of the necessary classes. This makes passing the values around to functions much easier. And, if you find you need to add another value, it can simply be added to the appropriate class, rather than needing to update the function and every where the function is called.

Share this post


Link to post
Share on other sites
That kind of thing definitely gets ugly and doesn't scale (as far as maintainability goes at least). Since you're posting it as an example, you're already aware of that.

I think every time this question comes up, a huge debate on global variables versus singletons ensues. I don't have the time or patience to get into that argument because they are always argued about as if the opposing side thought the other was a silver bullet.

What I *will* say, however, is that at the absolute very least, instead of letting naked booleans and such sit out there, they should probably be grouped together in the way that makes most logical sense to you. Perhaps a "static config" (something that doesn't change, that is read once from a config file of sorts) and a "dynamic config" (user-changeable settings and the likes, which can change).

How you control access to them depends on the constraints you want to put on your software, so that's up to you.

Share this post


Link to post
Share on other sites
Hey there,

I personally like to use 'smart' singletons, as desbribed below.
#include <memory>
class MySettings
{
private:
const static std::auto_ptr<MySettings> m_staticInstance;
tbthSettings();
public:
virtual ~MySettings();
static MySettings *getUniqueInstance(void) {
return m_staticInstance.get();
}
};

const std::auto_ptr<MySettings> MySettings::m_staticInstance(new MySettings()); // in cpp file...

IMO, they pretty much look like global variables, except that they have the advantages that classes have.
Cheers
StratBoy61

Share this post


Link to post
Share on other sites
Global variables are a Bad Thing in that they are commonly used by programmers who don't know how to do proper OO design but who don't know that they aren't doing proper OO design. A similar thing is true of singletons: People usually use singletons because they don't know how to avoid global variables (because they don't know proper OO design) but they think that singletons "don't count" as global variables.

Quote:
IMO, they pretty much look like global variables, except that they have the advantages that classes have.

You can have global variables with class type.

Share this post


Link to post
Share on other sites
Quote:
Original post by StratBoy61
Hey there,

I personally like to use 'smart' singletons, as desbribed below.
*** Source Snippet Removed ***
IMO, they pretty much look like global variables, except that they have the advantages that classes have.
Cheers
StratBoy61
Although this may work for you, I wouldn't be too quick to recommend the use of singletons as a solution to the global variable problem. There are many aspects of singleton usage that are less than obvious (not the least of which is order of construction and destruction).

@The OP: I would at least read this before deciding whether or not to make use of singletons in your own code.

Share this post


Link to post
Share on other sites
The Law of Demeter states that you should only use objects which you've:
  • Created yourself, or
  • Received as an argument.


The objective (and result) of this law is to reduce coupling between your modules, which in turn results in modifications being more local (you don't have system-wide dependencies to follow).

Therefore, if an object or method expects to manipulate something, it should either create it itself or expect to receive it as an argument (or constructor argument). Otherwise, the object becomes too reliant on external structure, which make its testing, reuse and modification much harder, and also creates a direct dependency between the object and the used element.

Neither global variables nor singletons are valid with respect with the law (and, in fact, neither is better than the other anyway, because they are destined to solve different problems anyway).

Share this post


Link to post
Share on other sites
eeerrr !!

OK so I'm going for a class or struct right ?

Now the question, which one ?

Structure, doesn't seem an improvement, other than, ... well .. it's structured !

Would be a bit more organized I guess:


typedef struct sWindow
{
bool fullscreen;
int scrWidth;
int scrHeight;
int scrBitDepth;

} myWindow;




example:
myWindow.fullscreen = TRUE;
myWindow.scrWidth= 1024;
etc etc

if anything this is more code no ??
having to put "myWindow." before everything

Share this post


Link to post
Share on other sites
1.) That's a C idiom for creating structs. In C++, you don't need the typedef:

// C idiom - works but can be considered deprecated
typedef struct point_
{
int x;
int y;
} point;

// C++ idiom
struct point
{
int x;
int y;
};




2.) I think you've missed the point. Avoiding global variables does not mean wrapping them in a struct(!) - rather, it involves designing your program in such a manner as they aren't needed. Pass them as arguments - use classes to encapsulate other objects which are tightly related to ('part of') themselves. A highly simplified example:


// Bad
#include <iostream>

int x;

void tripleX()
{
x *= 3;
}

int main()
{
x = 3;
doubleX();
std::cout << x;
}

// Good
#include <iostream>

int triple(int input)
{
/* Double is no longer tied to one variable - any int can be passed to it.
Also, we better abstract the concept of doubling a number - with the
result that the code is easier to reuse. */

return input *= 3;
}

int main()
{
int x = 3;
std::cout << double(x);
/* Also, a return value saves us a line of code in this example. Although,
this is of course also a case of design - it might sometimes not be
what we want! */

}




EDIT: D'oh.
EDIT 1306BST 25/03/07: Missed a semicolon

[Edited by - TheUnbeliever on March 25, 2007 6:27:55 AM]

Share this post


Link to post
Share on other sites
ok, I get what your saying.

Maybe I am doing something more dramatically wrong here.

just an example of various function I'm using:

CreateGLWindow(WND_TITLE, scrWidth, scrHeight) // in main

glOrtho(0, scrWidth, 0, scrHeight, -1, 1); // various times (fonts, hud, crosshair)

Draw_loadingScreen(scrWidth, scrHeight); // in render

Draw_creditsScreen(scrWidth, scrHeight); // in render

That's just an example of 2 variables (scrWidth, scrHeight)
I could list a dozen more, for mouse and key presses

Surely these need to be globals right ?

Share this post


Link to post
Share on other sites
No, they don't have to. Group all their usage as part of a single class, which will be responsible for storing and using them. Then, initialize an instance of that class and pass the values to it.

Share this post


Link to post
Share on other sites
hi ToohrVyk,

Yeah that's what I'm looking for.

I'm not sure what 'TheUnbeliever' was getting at, just sounded too simplified.

ok so I define a class


class Cwindow
{
public:
bool fullscreen;
int scrWidth;
int scrHeight;
int scrBitDepth;
};






//Then create an instance:
Cwindow myWindow;

//And assign some values:
myWindow.scrWidth = 1024;
myWindow.scrHeight = 768;

//and use them:
CreateGLWindow(WND_TITLE, myWindow.scrWidth, myWindow.scrHeight) // in main




What have I achieved apart from more code ?


And thinking that main.cpp is just the loop pretty much.
(init, loop (game state/render) , kill)

That still doesn't give me access to these variables from other units.
depending on time/level/state would depend where I am .. intro.cpp, level1.cpp, level2.cpp, credits.cpp

Share this post


Link to post
Share on other sites
Actually, you'd define a class which moderately encapsulates its properties:


class Window
{
std::string title;
int width, height;
public:
Window(const std::string & title, int width,int height)
:
title(title), width(width), height(height)
{
CreateGLWindow(title.c_str(), width, height);
// Other details related to creating a window
// with these properties
}

void Resize(int width,int height)
{
this->width = width;
this->height = height;
// Handle other details of resizing
}

int Width() const { return width; }
int Height() const { return height; }

~Window()
{
// Destroy a window with these properties
}
};




Then, you use it:


Window myWindow("The window title",1024,768);
Renderer myRenderer(myWindow);
World myWorld(myRenderer);
myWorld.load("1.lvl");




EDIT:
intro.cpp, credits.cpp — no reason to know the width and heigth as global variables. Just pass it a target window.
level1.cpp, level2.cpp — why would you need width and height at all?

Share this post


Link to post
Share on other sites
Quote:

What have I achieved apart from more code?


You have achieved that the Window width&height are not accessible by anywhere, but only by modules related to CWindow. Your code is more organized, easier to extend, and if you get some kind of bug/problem with width&height, you know where to look(Window class), instead of the whole program.

Btw, I would implement CWindow like this(Assuming that CreateGLWindow is modified to return an HWND value instead of storing it in a global):


class Window
{
private:
int scrWidth;
int scrHeight;
int scrBitDepth;
HWND hwnd;
std::string title;
public:
Window(std::string title,int width,int height,int depth):title(title),srcWidth(width),srcHeight(height),srcBitDepth(depth)
{
hwnd=CreateGLWindow(title,width,height);
}
};





And just use it like this:

Window mywindow("My window",1024,768);

-EDIT: Beaten...

Share this post


Link to post
Share on other sites
maybe someone could share a sample project (visual studio) to render an OpenGL blank Window, with this kind of structure or framework.

I'm not looking for anyone to write a game for me, but I need a framework to learn from, I will pick it up better like that.

NeHe's tutorials are so out-dated, half the other examples are ALL (or mixing) C and C++.

No real problem, with the game logic, and openGL, I guess just getting to grips with this C++, and 'better' programming skills is what I need.

Don't get me wrong I have a very functional openGL program, cameras, lighting, music, sound FX, crosshairs, HUD, fonts, the works.

The more I read, I am realizing some of my programming methods might not be the best ;-)

So if anyone is kind enough to share a good structured framework.
Just as we have been discussing, no globals, better class/structures.
(and I've been using chars* not strings) slap on the wrist.

I would appreciate it ;-)

Share this post


Link to post
Share on other sites
Original post by dazedandconfused
typedef struct sWindow
{
bool fullscreen;
int scrWidth;
int scrHeight;
int scrBitDepth;

} myWindow;


[/quote]You and others in this thread seem not to understand what this code actually does.

It creates a struct called sWindow, and creates a global instance of that stuct called myWindow. Except that it is global to each file that includes it, but not to all all the files together. And it is still possible to create other instances of sWindow.

That is surely not what you want.

And as has been mentioned, the "typedef" is superfluous there in C++.

Share this post


Link to post
Share on other sites
Quote:
Original post by King Mir
It creates a struct called sWindow, and creates a global instance of that stuct called myWindow.


It does no such thing. It creates a struct named sWindow - and creates a typedef to that struct named myWindow. It instantiates nothing.

i.e.

typedef struct sWindow
{
bool fullscreen;
int scrWidth;
int scrHeight;
int scrBitDepth;

} myWindow;

int main()
{
sWindow foo; // Fine
myWindow bar; // Fine

myWindow.scrWidth = 800; // Error! myWindow is a type - not an object
}




Indeed, it is equivalent to:

struct sWindow
{
bool fullscreen;
int scrWidth;
int scrHeight;
int scrBitDepth;

};

typedef sWindow myWindow;



EDIT 1305BST 25/03/07: Typo

[Edited by - TheUnbeliever on March 25, 2007 6:15:34 AM]

Share this post


Link to post
Share on other sites
No wonder we get confused in this place !!

(no offence to GameDev by that [wink])

But do you see my point, some of you guys have been members of this forum for 3-5 years.

Who's advice should we take.

I appreciate everyone's help, wiithout a doubt, trust me.

And multiple opinions and advice is better than one.
Which is why I'd rather hear it from you "seasoned pro's", instead of just reading the word of one author in a book.

But what do I do [headshake]

Note: ok let's forget the 'C' or 'C++' debate, I followed some old code.
I'm working on C++ to clear things up, if I have made mistakes, I'm working on rectifying that. Hence why I'm here.

Let's work on the problem assuming I'm trying to code in C++

Cheers ;-)

Share this post


Link to post
Share on other sites
In this particular instance, King Mir is wrong, period. He's not far from being right; he just misunderstood the meaning of the typedef keyword in this context, possibly because he isn't familiar with this (somewhat strange) idiom, which does not exist in C++. In situations like this, of course, you can just test it out yourself and see.

In the larger question of "who should you trust" there's no easy answer, unfortunately. Opinions over software engineering which don't come down to actual syntactic meaning are just that: opinions. Try stuff out for yourself, keep an open mind, never assume you know everything.

Share this post


Link to post
Share on other sites
ok to keep things simple this just uses the console and im typing it in here without testing so if theres bug's sorry but you should be able to get the basic idea from it.

Edit: i should mention, its the example you asked for above demonstrating how I avoid globals etc..


//main.cpp

#include "renderer.h"
#include "game_world.h"
#include <iostream>

int main()
{
// create a renderer that renders to a window 80 * 40 in size
renderer renderer(std::cout, 80, 40);
game_world game(renderer, std::cin);

game.run();
}




// renderer.h

#include "vertex.h"
#include <boost/utility/non_copyable.hpp>
#include <vector>
#include <iostream>

struct renderer : boost::non_copyable
{
typedef std::ostream window_type;

renderer(window_type& window, std::size_t width, std::size_t height);

void point(vertex v);
void line(vertex v1, vertex v2);
void triangle(vertex v1, vertex v2, vertex v3);
void square(vertex top_left, std::size_t size);
void rectangle(vertex top_left, vertex bottom_right);
void text(std::size_t x, std::size_t y, const std::string text);

std::size_t screen_width();
std::size_t screen_height();

void bitblt();

private:
typedef std::vector<char> row_type;
typedef std::vector<row_type> buffer_type;

struct point_drawer
{
point_drawer(renderer& renderer, std::size_t x, std::size_t y):
renderer(&renderer),
x(x),
y(y)
{ }

void operator()(vertex::color_type c)
{
renderer->point(vertex(x++, y, c));
}
};

buffer_type buffer;
window_type& window;

static void clear_row(row_type& row);
static void display_row(const row_type& row, window_type& window)
};





// renderer.cpp

#include "renderer.h"
#include <algorithm>
#include <iterator>

renderer::renderer(renderer::window_type window, std::size_t width, std::size_t height):
window(window)
{
// allocate height rows
buffer.resize(height);
// and resize each row to be width units wide
std::for_each(buffer.begin(),
buffer.end(),
std::bind2nd(std::mem_fn(&buffer_type::resize), width));
// clear the back buffer
std::for_each(buffer.begin(),
buffer.end(),
&clear_row);
}

void renderer::point(vertex v)
{
if (v.x < screen_width() && v.y < screen_height())
{
buffer[v.y][v.x] = v.color;
}
}

void renderer::line(vertex v1, vertex v2)
{
// http://en.wikipedia.org/wiki/Bresenham's_line_algorithm

bool steep = std::abs(v2.y - v1.y) < std::abs(v2.x - v1.x);
if (steep)
{
std::swap(v1.x, v1.y);
std::swap(v2.x, v2.y);
}

if (v1.x > v2.x)
{
std::swap(v1, v2);
}

int delta_x = v2.x - v1.x;
int delta_y = std::abs(v2.y - v1.y);
int error = 0;
int ystep = v1.y < v2.y ? 1 : -1;

for (vertex current = v1; current.x < v2.x; ++current.x)
{
if (steep)
{
point(vertex(current.y, current.x, current.color));
}
else
{
point(current);
}

error += delta_y;
if (error > 2 * delta_x)
{
y += ystep;
error -= delta_x;
}
}
}

void renderer::triangle(vertex v1, vertex v2, vertex v3)
{
line(v1, v2);
line(v2, v3);
line(v3, v1);
}

void renderer::square(vertex top_left, std::size_t size)
{
rectangle(top_left, vertex(top_left.x + size, top_left.y + size, top_left.colot);
}

void renderer::rectangle(vertex top_left, vertex bottom_right)
{
vertex top_right(bottom_right.x, top_left.y, (top_left.color + bottom_right.color) / 2);
vertex bottom_left(top_left.x, bottom_right.y, (top_left.color + bottom_right.color) / 2);

line(top_left, top_right);
line(top_right, bottom_right);
line(bottom_right, bottom_left);
line(bottom_left, top_left);
}

void renderer::text(std::size_t x, std::size_t y, const std::string text)
{
std::for_each(text.begin(),
text.end(),
point_drawer(*this, x, y));
}

std::size_t renderer::screen_width()
{
return buffer[0].size();
}

std::size_t renderer::screen_height()
{
return buffer.size();
}

void renderer::bitblt()
{
std::for_each(buffer.begin(),
buffer.end(),
std::bind_2nd(&display_row, window));
window << std::endl;
}

void renderer::clear_row(renderer::row_type& row)
{
std::fill(row.begin(), row.end(), ' ');
}

void renderer::display_row(const renderer::row_type& row, renderer::window_type& window)
{
std::copy(row.begin(), row.end(), std::ostream_iterator<char>(window));
window << '\n';
}





// game_world.h

#include "renderer.h"
#include <iostream>

struct game_world
{
game_world(renderer& renderer, input_type& input):
renderer(renderer),
input(input)
{ }

void run();

private:
typedef std::istream input_type;

renderer& renderer;
input_type& input;

void diplay_menu();
void process_input();


static vertex read_point(input_type& input)

enum
{
point = 1,
line,
traingle,
square,
rectangle,
exit
};
};





// game_world.cpp

#include "game_world.h"

game_world::run()
{
bool finished = false;
while (!finished)
{
diplay_menu();
finished = process_input();
renderer.bitblt();
}
}

vertex game_world::read_point(game_world::input_type& input)
{
std::size_t x, y;
vertex::color_type color;
input >> x >> y >> color;
return vertex(x, y, color);
}

game_world::diplay_menu()
{
renderer.text(0, 0, "Select an Option:");
renderer.text(4, 2, "1) Draw Point.");
renderer.text(4, 3, "2) Draw Line.");
renderer.text(4, 4, "3) Draw Triangle.");
renderer.text(4, 5, "4) Draw Square.");
renderer.text(4, 6, "5) Draw Rectangle.");
renderer.text(4, 7, "6) Exit.");
renderer.bitblt();
}

game_world::process_input()
{
int selection;
input >> selection;

switch (selection)
{
case point:
renderer.point(read_point(input));
case line:
renderer.line(read_point(input), read_point(input));
case triangle:
renderer.triangle(read_point(input), read_point(input), read_point(input));
case square:
{
vertex v = read_point(input);
std::size_t size;
input >> size;
renderer.square(v, size);
}
case rectangle:
renderer.rectangle(read_point(input), read_point(input));
case exit:
return true;
}

return false;
}





// vertex.h

struct vertex
{
vertex(std::size_t x, std::size_t y, char color):
x(x),
y(y),
color(color)
{ }

std::size_t x, y;
char color;
};


Share this post


Link to post
Share on other sites
Quote:
Original post by jyk
There are many aspects of singleton usage that are less than obvious (not the least of which is order of construction and destruction).
@The OP: I would at least read this before deciding whether or not to make use of singletons in your own code.

Mmm, I do not want to start a debate here, but I would like to make sure that we are on the same page :
First, the creation of a singleton can easily be controled. Only the *smart* aspect of the singleton (in my example) makes its creation difficult to control, agreed.
Second, I read the post that you recommended, and I found the following statement to be quite confusing :
Quote:
"So how can the programmer guarantee that multiple instances of the object won't be created? Quite simply, you don't."
In my example, the constructor is private (even though I mispelled the class' name), and I believed it should insure that multiple instances could not be created (in a single thread env.), right ?

In my opinion, there are at least two good reasons to use singletons (and thus yes, global objects) :
1) optimization
2) object's scope and lifetime : If the object's lifetime is the same as the whole program, then it makes half sense to be a global object. If on top of that, it has to be accessed anywhere in the code, then it makes the second half sense. Think of a database accessor object for instance.
Cheers
StratBoy61

Share this post


Link to post
Share on other sites
Quote:
Original post by StratBoy61
1) optimization


Dubious. True global objects need synchronization scaffolds to be usable in any industry-sized project, which would hinder performance. The only advantage would be saving on argument-passing, which can never be fully accomplished because of the necessary __thiscall in most cases. And either way, it's easier to make an object global than to un-make it, so profile first, and make global last.

Quote:
2) object's scope and lifetime : If the object's lifetime is the same as the whole program, then it makes half sense to be a global object. If on top of that, it has to be accessed anywhere in the code, then it makes the second half sense.


If the object has to be accessed anywhere in the code, then you have a major design problem. The only solution in this kind of situation, if you are serious about writing any remotely maintainable project, is to stop writing code immediately and starting to think about a full redesign of the involved object. The strength of module coupling introduced by project-wide global variables is too strong to bear in many projects.

The usual consequence is that the project bursts into flames in beta-testing, when testers start demanding a feature, and you're faced with either spending a week un-globalizing half the code (and doing nothing else) or refusing the feature altogether. On industry projects, this happens even sooner, when the client changes requirements, and you dive into refactoring for a few days, never to be seen again.

However, if the object's lifetime is the same as the whole program, it makes full sense to make it a global object. Just remember that the problem with globals lies not in creating them, but in accessing them. If your global variable is accessed once in the entire program, and passed as argument everywhere else, everything is fine. Things start to hurt when you have global variable accesses all over the code.

Quote:
Think of a database accessor object for instance.


Which is actually a bad idea in a large project, where the database server could be split into several servers to split the load. Yes, I've had this happen, and I was happy to have a protective abstraction layer between the server access and the data usage. If you're confident that your project is throwaway and will not be used in a serious fashion ever, then fine. In the industry, this kind of practice is a big flashy red sign reading "rewrite half the system next year".

Share this post


Link to post
Share on other sites
Quote:
Original post by StratBoy61
In my opinion, there are at least two good reasons to use singletons (and thus yes, global objects) :
1) optimization
2) object's scope and lifetime : If the object's lifetime is the same as the whole program, then it makes half sense to be a global object. If on top of that, it has to be accessed anywhere in the code, then it makes the second half sense. Think of a database accessor object for instance.


Optimization should never be a part of design discussion. Scalability and overhed yes, but not optimization.

The database accessor isn't best example either. Typical SQL accessors revolve around connections and connection specific objects that live only for the duration of the connection. Also, most connections cannot be shared between threads, they need to be recreated if a connection error occurs, and similar. Methods for accessing it are obviously global, but not much else.

Singletons and globals have their place. But in even basic OO application, they should be very limited.

I have ran across an application converted from C into C++. It was fully object oriented. It contained several hundred classes, all singletons, containing C code, and named after the file they were in.

The final decision about what is a singleton and what isn't comes down do separating function from form.

A singleton should also be able to start-up its own internal state at any time for any reason, without external dependencies. If it depends on other objects, then it'll make a poor singleton, since it may cause random failures at most unusual times. If singleton isn't able to perform it's own state initialization, or is initialized manually, then it's not a singleton, just a global variable.

Going by the above example, even if window size won't change during application life-time (is this true?), it will be initialized after some event, and most likely depends on several other pre-conditions (validating the driver, checking for resolution, allocating handle, ...). And that's a lot for a singleton to handle.


int FPSCount = 0;
char* FPSCountTxt = "";
DWORD timeCurrentTime = 0;
DWORD timeDemoStartTime = 0;
DWORD timeElapsedTime = 0;
char* timeElapsedTxt = "";



This may seem like good candidate for singleton, but it really isn't. There's a special DemoStartTime. So there's a difference on where these variables will be used.

Going back to use of this class. Where will it be called? There's bound to be a render loop that will time the rendering process. And the measurment becomes a member of the window class. Each window has its own measurement. Even more, the fps monitor should be part of scene definition, since this is what we're interested in. Time when scene was started, number of frames rendered, etc. On top of that, you can of course add extra global counter for application life-time.

// Testing Variables
bool showHelp = FALSE;
bool showWireframe = FALSE;
bool showFog = FALSE;
bool enableLighting = TRUE;
bool enableSound = TRUE;
bool enableFrustum = TRUE;



These are also not globals. In order for these variables to have any effect, the rendering system needs to be between fully initialized and not being destroyed. And that is much less than full application life-time.
These variables are prime candidate for being completely hidden within renderer class, and being changable only though accessors. Even more, this allows you to query device capabilities and log any problems with setting these flags.

bool		MouseMove = FALSE;    
int MouseButton = -1;
//Mouse Position
GLfloat MoveXcoord, MoveYcoord;
int clickXcoord, clickYcoord;


Mouse events seem like a good idea for a global. Until you need to write a demo that will simulate mouse moves and mouse clicks. Who will then have the authority, system mouse event driver, or your demo driver? And how will you disable one of them? So these must definitely not be globals of any kind - even more, all these variables are extremly short live, ideally, their life-span 0 seconds, since they become invalid the moment system passes them on. From design perspective, these are prime candidate for either method parameters, or message queue.

There is really very little reeason for globals - ever. I suppose the only ones that really warrant such would be invariants. Everything else goes against OO design (except valid exceptions).

Share this post


Link to post
Share on other sites
Quote:
Original post by derickdong
When people tell you something is bad or wrong (programming-wise), they usually mean that you shouldn't use it unless the alternative is worse. For instance, many sources will tell you that goto should never be used. However, there are times when it can be useful.

Useful, perhaps, but usually simple refactoring is a better alternative.

Quote:
In my opinion, there are at least two good reasons to use singletons (and thus yes, global objects) :
1) optimization

Uh?
Singletons are not faster. Globals are not faster either. (True, you save having to push a few bytes onto the stack when calling a function, but in return you lose data locality which will give you cache misses, and the compiler is unable to perform many optimizations on a global because it can be accessed from anywhere, so it has to be written to memory as soon as it's updated. It's no good just holding the new value in a register, because then other parts of the code may just decide to access the obsolete version in memory.

Quote:

2) object's scope and lifetime : If the object's lifetime is the same as the whole program, then it makes half sense to be a global object. If on top of that, it has to be accessed anywhere in the code, then it makes the second half sense. Think of a database accessor object for instance.
[/quote]
I fail to see the connection between lifetime and scope. An objects lifetime may be the same as the program, but that doesn't mean it should be visible to every part of the program. And if something has to be accessed anywhere in the code, you have a really bad design.
I'd certainly hate for all parts of my program to be able to access a database.

Share this post


Link to post
Share on other sites
WOW,

this is getting a big thread !!

I am trying to follow this, honest.

Good to hear so many views.

Well let's give another example, since Antheus quoted the timing code.

MAIN.CPP

timeDemoStartTime = GetTickCount();

while(!done)
{

// main loop
// PeekMessage

// render
// get FPS and times

// process mouse
// process keyboard
}





ENGINE.CPP

void getTimes()
{
timeCurrentTime = GetTickCount();
timeElapsedTime = timeCurrentTime - timeDemoStartTime;
// calculate FPS
}





So surely "timeDemoStartTime" needs to be global ?

Or like some say, passing parameters, not globals.
Do I need a function that returns time.

in MAIN.CPP
timeDemoStartTime = getStartTime();

in ENGINE.CPP

DWORD getStartTime()
{
return GetTickCount();
}





That could reduce some of the required globals, but not all.

** EDIT

Even if I look at NeHE new beta frame work it has problems.
Ahhhh BETA I hear you shout, well don't count on it being fixed lol.
I have followed and learnt a great deal from NeHe's tutorials, but I am told half of it is actually 'C' and not 'C++' (char is C right among other problems)
And yeah great, no globals .. code looks good.

Only because half of it is 'Hard Coded', like the problem here with screen Width and Height, he has only got round it, by Hard-coding it 3 times.

[Edited by - dazedandconfused on March 25, 2007 9:48:00 AM]

Share this post


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

  • Advertisement