Can you correct this design

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

Recommended Posts

I tried making a oop design for my game but i think there is really something wrong with it. I'm using OpenGL, C++ and GLUT to make my game. My game is a copy of the game lightcycle (just search for details its long if i explain,sorry). Well my design goes like this. i have four class (w/ three as the main and one is just inherits the first one): Window Class: -well this class is for initializing the window system of the game although its use is just to for basic accessor methods and init methhods,this is used as a base class to inherit a more specific windowing system. glutWindow Class -this class inherits from Window.basically it just initializes the glut window and has the inherited method of setting the size of the window Cycle Class -well this class takes care of initializing and drawing of the cycle in the game Game Class -this is where i put the games logic,drawing screen,and input reading.the problem is that glut require callback registration,but all my callback 'to be' are in my game class but that gave error when i put in my class(ie if i put glutKeyboardFunc(this->keyHandler); to my Game::keyHandler definition) so the turn around that i thought of is to make a function holder,here how it goes: if i need to connect my Game::keyHandler to glut i make a function(namely kewyHandlerFunc) that has only "g->keyHandler;" in it (g is the object of Game) and register in main glutKeyboardFunc(keyHandlerFunc); so far it works but i feel something is wrong...also when i run glutMainLoop() i cant delete the Game Object,so i did is that i assign a function to atexit() where it deletes the Game object...and initializing the game can be seen on the main function...what i really want to only see on main is this:
#include "Game.h"

int main( int argc, char **argv )
{

Game g= new Game( &argc, argv , "Beam Cycle" );

if ( g->init() )
{
g->run();
}
else
{
std::cout << "initializiation failed!" << std::endl;
}

delete g;

return 0;
};


Please help me with this design...if you need the code just say it and ill post it...really need to make this work PS its required that i use GLUT..so cant change it

Share on other sites
Game g= new Game( &argc, argv , "Beam Cycle" );

You forgot g would be a pointer.

Anyway, could you show the way you implemented your architecture right now? As a graphical representation.

And explain your "Cycle" class a bit more.

Share on other sites
Quote:
 Original post by Skeezixthe problem is that glut require callback registration,but all my callback 'to be' are in my game class but that gave error when i put in my class(ie if i put glutKeyboardFunc(this->keyHandler);
In C++ there is an almost painful distinction between a pointer to a free function and a pointer to a member function. GLUT requires a pointer to a free function but Game::keyHandler is a member function which is why it doesn't work.
Note however that a static member function behaves like a free function, so if you can make Game::keyHandler a static function then it will be possible to register it with GLUT. The caveat is that static member functions cannot access non-static members of a class.

You have to realise that GLUT is a C library and it won't play nicely with C++ classes, only C-style functions (and static member functions), so you are going to have to put up with a sub-ideal design to use it.

Quote:
 what i really want to only see on main is this:*** Source Snippet Removed ***
Some points to make here:

1) Don't use pointers needlessly.
2) Don't separate construction and initialisation.
3) Do use exceptions.

So with that in mind something like this makes more sense:

int main(){    try {        // Instantiate the game, load the configuration from a file rather        // than from the application arguments.        Game g(Settings("config.txt"), "Beam Cycle");        // Run the game        g.run();    }    catch (...) {        std::cout << "Something Failed!" << std::endl;        return -1;    }    // return 0 is implicit}

Share on other sites
If I were making this design, I would have some system of getting input defined in the Window Class. Then the glutWindow Class should handle the glut way of getting input internally, and provide that data to other classes through a strictly defined interface in the base Window Class. This way there would be no dependency between GLUT and the Game Class.

Share on other sites
Quote:
 You forgot g would be a pointer.

sorry my mistake

Quote:

its just for example,anyway i noted it :)

Quote:
 Anyway, could you show the way you implemented your architecture right now?

well,Window Class is a general class of window system(but for now it can only initialize and set its own size),glutWindow inherits form Window and changes how it(glutWindow) initializes itself(it uses glutInit,glutInitDisplayMode, glutInitWindowSize,glutInitWindowPosition,glutCreateWindow w/ properly supplied parameters) then it its SetSize function it uses glutReshapeWindow(again with proper parameters)

Quote:
 And explain your "Cycle" class a bit more.

the Cycle Class is a class contains the current x and y position of a cycle,its velocity,direction facing,if its destroyed,width of its trail and its trail(w/c are x&y points in a vector)

it also has a draw function which is basically just opengl,glut commands to draw circles translated and scaled to look like a bike :),also in that draw function i draw the trail using lines with certain widths...so if you call bike->draw() the bike(with proper facing due to direction member var) and trail are drawn

for the Game class it contains a glutWindow object,two cycle var,pause var(bool),framedelay var

this class contains other glut functions(glutKeyboardFunc,glutSpecialFunc, glutReshapeFunc,glutDisplayFunc, glutIdleFunc) that i pass external function but in that function it only contain member function call from the Game class..because if i make my member function static i wont be able call objects that aren't static

Quote:
 If I were making this design, I would have some system of getting input defined in the Window Class. Then the glutWindow Class should handle the glut way of getting input internally, and provide that data to other classes through a strictly defined interface in the base Window Class. This way there would be no dependency between GLUT and the Game Class.

i thought of that but glut only requires a function to be passed to glutKeyboardFunc, and i think that the game class should contain the input reading cause games has its own input translations and i maintain the window and thus glutWindow class as a window only....can you elaborate on this more...

Share on other sites
So if your game class contains glutWindow object (not a pointer to your base window class), then it voids the need for your window class in the first place.

If you will ever create another window class implementation, such as using plain win32 for initializing the OpenGL window or something, you'll find that your game class is dependent on the way how GLUT calls its callbacks and your new window class will have to emulate that convention. If this is fine by you, then go for it.

On the other hand, you can architect your system a bit differently. You can define an interface for getting access to input data in your base window class, implement it in your glutWindow class and then access the input through that interface. Or if you want to have event like callbacks in your game class, then make an event system, which would accomplish that.

If you're going to go with the GLUT method, you can have a static game pointer in the game class, which would point to this of the game class object (assuming that only one instance of game exists). Then you could access the members through that pointer. I'm certain that you will be able to access the public members through that pointer, but I'm not sure if you'll be able to access the protected and private members. If you're not able to access those members, this probably can be solved using friendship. Haven't ever tried that though.

[Edited by - Giedrius on August 12, 2008 6:08:43 AM]

Share on other sites
can you post some pseudo codes,i get the point about the base window class and having input interface for it but want im confused is that if in case i already have an input interface in my base window but glut needs to use either glutKeyboardFunc or glutSpecialFunc(or else if i missed something),so how do i do that if what i need is a function that needs to be passed to these functions,actually that was one of my first plan...to pass a member function of glutwindow to the input functions of glut(keyboard and special) but as i have said it cant be done because it needs static functions...

Quote:
 Or if you want to have event like callbacks in your game class, then make an event system, which would accomplish that.

i haven't dwell on this topic much so i think i need further explaining on this..

Share on other sites
You might want to look into the other options for window setup and input handling. I'd recommend GLFW, SDL or SFML. SFML is newer and C++ while the other are more mature and C based. All of them are (IMHO) better choices than GLUT.

Your game class seems pretty heavy. It has a lot of responsibility. You might want to consider breaking it out into several smaller classes with well defined roles. If you want a single interface to them, you could use the facade pattern to create a class to wrap them all, but that's up to you.

Cheers,

Bob

Share on other sites
As I don't know how exactly you are architecting your system, I'll keep the samples simple, so you can adapt and extend them to your needs.

class CWindow{public:	virtual bool IsKeyDown(int key) = 0;	//...};class glutWindow : public CWindow{private:	static void glutKeyHandler(<glut params go here>);	static bool m_bKeys[256]; //true if the key is down, false otherwisepublic:	bool IsKeyDown(int key)	{		return m_bKeys[key];	}};void glutWindow::glutKeyHandler(<glut params>){	if (<key> was pressed) m_bKeys[key] = true;	else if (<key> was released) m_bKeys[key] = false;}bool glutWindow::m_bKeys[256];

or

class glutWindow : public CWindow{private:	static void glutKeyHandler(<glut params>);	static glutWindow * pThis;	bool m_bKeys[256];public:	glutWindow()	{		pThis = this;	}	bool IsKeyDown(int key)	{		return m_bKeys[key];	}};void glutWindow::glutKeyHandler(<glut params>){	if (<key> was pressed) pThis->m_bKeys[key] = true;	else if (<key> was released) pThis->m_bKeys[key] = false;}glutWindow * glutWindow::pThis;bool glutWindow::m_bKeys[256];

As for the events, you can have an IEvent abstract interface class which should be inherited by classes who need to get some kind of event (such as a keypress). Then the class should register itself with the class that dispatches the event, so when the event occurs, the dispatcher class knows which object to send the event to.

Share on other sites
so basically you just register glutKeyHandler(<glut params go here>); to glutKeyboardFunc then the game class queries the keys using IsKeyDown,then i handle the effects of pressing that key in the Game class...where do i register glutKeyHandler(<glut params go here>); to glutKeyboardFunc,i mean where do i put this statement glutKeyboardFunc(glutKeyHandler); in the Game class or in the window class??

another problem now is the drawing function...same as input reader but with more problem...if i make this function static i wont be able to call the Cycle objects' (included as member var of Game class) draw function in it,w/c lost its purpose because as of now that's the only thing i draw in the screen,i think this goes the same with the update func(w/c contains the logic of the game),if i make it static i wont be able to call any function of cycle class(or any class that is not static)

Share on other sites
You should register the keyboard handler in the GLUT window class. glutKeyboardFunc(glutWindow::glutKeyHandler);

You should create your drawing function (and all the other GLUT functions) simillarly to the keyboard function and have your glutWindow (or better yet, the base window class) have a pointer to a game object which could be set during the construction of the window class. This way it would be a little simplier. When handling the keyboard you could call m_pGame->KeyPressed(key); and when you need to draw, call m_pGame->Draw();. Then the game class will be responsible for drawing everything that is needed.

Share on other sites
Quote:
 Original post by dmatter2) Don't separate construction and initialisation.

Why is that ? I like reading advices, but I like better when they are explained :) I agree with most of your post, but I don't understand why you should separate construction and initialisation ?
In my experience, it can be usefull : for complex classes, you can't give all the parameter in the constructor, so creating the instance, setting all its parameter, and only then calling the initialization method can be usefull.

I'm curious to know the reasons behind this advice :)

Share on other sites
Quote:
Original post by paic
Quote:
 Original post by dmatter2) Don't separate construction and initialisation.

Why is that ? I like reading advices, but I like better when they are explained :) I agree with most of your post, but I don't understand why you should separate construction and initialisation ?
In my experience, it can be usefull : for complex classes, you can't give all the parameter in the constructor, so creating the instance, setting all its parameter, and only then calling the initialization method can be usefull.

I'm curious to know the reasons behind this advice :)

If you separate construction and initialization, you have a period between the two where the object exists in an incomplete state. For as much as it's possible, aim for always having the object in a well defined, useful and complete state. Since construction is always followed by initialization, it makes sense to let the construction be the initialization at the same time.

Now your second point about having many parameters to set can give you quite ugly and long constructor calls. That, however, can be solved with a separarte parameter structure. Something like this.
struct GameParameters{    // variables, functions and anything you need to set game state parameters.    // use for example constructors to set default values and setters to set additional parameters};struct Game{    Game(const GameParameteres &p)    {        // all constructor parameters are now gathered nicely in p    }}int main(){    GameParameters p;    p.setParam1(...);    p.setParam2(...);    Game g(p);    g.run();}

Here g is always in a valid state, and you don't have many parameters to its constructor. Parameters are gathered nicely in a separate structure you can modify dynamically (my asking the user for parameters, or reading from file, for example) before constructing the game object.

Share on other sites
However it is sometimes required to delay the initialization until a certain event in the program flow has happened. Say that you have a global class object which needs a handle to a window or something, but when the global object is constructed, the window hasn't been created yet. So the initialization must be delayed until the window was created.

Share on other sites
Quote:
 Original post by GiedriusHowever it is sometimes required to delay the initialization until a certain event in the program flow has happened. Say that you have a global class object which needs a handle to a window or something, but when the global object is constructed, the window hasn't been created yet. So the initialization must be delayed until the window was created.

Yes another great reason to avoid globals [smile]

Share on other sites
Quote:
 Original post by GiedriusHowever it is sometimes required to delay the initialization until a certain event in the program flow has happened. Say that you have a global class object which needs a handle to a window or something, but when the global object is constructed, the window hasn't been created yet. So the initialization must be delayed until the window was created.

If you need to initialize it at some specific point, just create it there also.

Share on other sites
Or consider if the object needs to do some initialization which may fail. You can't return anything in a constructor, so you would need to also store the status inside the class and then check that status after the construction. Of course this might be a good thing to do, but might be a bit burdensome for the programmer outside of the class code in some situations.

Quote:
 If you need to initialize it at some specific point, just create it there also.

I didn't understand what you mean by this. Can you show a small example?

P.S. I'm not using global objects a lot myself, but just wanted to show the possible situations where seperate intialization is needed.

Share on other sites
Quote:
 Original post by GiedriusOr consider if the object needs to do some initialization which may fail. You can't return anything in a constructor, so you would need to also store the status inside the class and then check that status after the construction. Of course this might be a good thing to do, but might be a bit burdensome for the programmer outside of the class code in some situations.

Use exceptions. Also note that parameter checking may, to some extent, be done in the parameter structure also. For example, if the construction of your class would fail if a file is missing, then you can check for that file when setting that parameter. Or you can do a parameter check once everything is set.

Quote:
Original post by Giedrius
Quote:
 If you need to initialize it at some specific point, just create it there also.

I didn't understand what you mean by this. Can you show a small example?

P.S. I'm not using global objects a lot myself, but just wanted to show the possible situations where seperate intialization is needed.

Just loook at my example above and place the definition of g where you want it. You can place as much code as you like between setting the parameters to p and creating the object g.

But if you absolutely have to use a global object (see rip-off's comment above on that), use a (smart) pointer.
boost::shared_ptr<Game> g;int main(){    GameParameters p;    p.setParam1(...);    p.setParam2(...);    g.reset(new Game(p));    g->run();}

Share on other sites
@Giedrius if i put all other GLUT functions on the glutWindow class then it like i could make my game without the Game class....and it would destroy my thinking of a GAME HAS A WINDOW(or my thinking is wrong)...instead it will turn out to be A WINDOW HAS A GAME...

Share on other sites
It depends on how you will implement it. What I am saying is that all the glut specific functions and other stuff should be implemented in the glutWindow class. You can have a pointer to the game class inside the window class which could get set during the construction of the window. Then the window class could call all the relevant functions in the game class, like m_pGame->KeyPressed(key), m_pGame->Draw(), etc... This way the game would contain the window and the window would only have a pointer for calling the functions in the game class.

Share on other sites
Am I the only one who doesn't create a window class?

I put my window code in my main.cpp file.

Is the idea of creating a windows class so it can be used in other apps or just oop neatness?

Share on other sites
On the topic of construction and initialization:

Can you all clarify what you mean by "construction"? Are you talking about the "building" of the object, or of the allocation of memory, calling of constructor, and that kind of stuff.

For example...

We're making a random maze generator. I have a generic Maze class. It has a constructor which initializes it to a maze of the given dimensions where all possible places are walls. I then have a static member function of Maze CreateRandomPerfectMaze(), which creates a new Maze, and then sets its walls so that it is a random, perfect maze.

In this example, what is the construction of the Maze? Is it the c'tor of Maze or the CreateRandomPerfectMaze()? Do you consider the initialization and construction of the maze to be separate or together?

Another more generic example: I've got a class and I want to load its state from an XML file. I call a default c'tor which puts it in a well-defined, initialized, but largely useless state, then I read in the XML file and set the class's properties appropriately. Same questions as above.

Thanks.

@Anda: I used to just put the window code in main(), but I've since switched to a Window class. Partly I find it more consistent (eg: physics engines have a Simulation() class, or w/e)., but mostly I just find that it clears up the main() function, which tends to be overly large and messy.

Share on other sites
Quote:
 Original post by EzbezI've got a class and I want to load its state from an XML file. I call a default c'tor which puts it in a well-defined, initialized, but largely useless state, then I read in the XML file and set the class's properties appropriately. Same questions as above.

So your code looks like this:

Obj obj;obj.load(fileName);

What is the benefit of first creating an "empty" object and only initializing it later? In other words, why not just do this:

Obj obj(fileName);

The disadvantage of the former is that you can forget to call the load() function, and then you'll be working with an uninitialized object. This is basically why constructors exist.

The disadvantage of the latter is that if you need to delay the creation of the object or you need to be able to recreate the object (that is, destroy it and create it with a different state), you'll need to use dynamic memory allocation, which I suppose can be an issue sometimes.

(although for the re-creation of the object, you might be able to use a memory pool.)

Share on other sites
Sorry to Paic and others, this thread seems to have slipped under my radar since my first post; as for 'why' separating construction from initialisation is bad I think Brother Bob has it covered beautifully.

I'll tackle some other things though.

Quote:
 Original post by andaI put my window code in my main.cpp file.
If not using a class I would probably stick it in a separate function in it's own file and make a call to it from main.

Quote:
 Is the idea of creating a windows class so it can be used in other apps or just oop neatness?
Both.
You wouldn't use a class for the sake of using a class though; if it can be done sensibly within a simple function with no state, as I think GLUT can be, then it makes sense to do so. A class is just a collection of functions and state all combined into one unit of code.

I don't think having a GLUTWindow class makes complete sense however, as far as I know you cannot have more than one GLUT window and you don't need to hold onto any variables when using it. I stand to be corrected here though.

I don't use GLUT for windowing for the same reasons that Skeezix is having problems: In order to do anything remotely non-trivial you have to resort to global or static variables - take the glut key handler function as an example, the only way for it to modify the state of the rest of the program in response to a keypress would be to access some global, or static, variable. The GLUT windowing API just isn't sophisticated, or modern, enough to allow for any of the nicer OO options. In comparison the Win32 library, despite being both old and a C (not C++) library, does have the sophisticatation to avoid globals and statics which allows you to wrap things up in a more OO way.

Quote:
 Original post by EzbezCan you all clarify what you mean by "construction"? Are you talking about the "building" of the object, or of the allocation of memory, calling of constructor, and that kind of stuff.
By construction I/we mean object creation, also termed instantiation (creating an instance of a type).
When you create an object, it gets memory allocated for it, name bindings are created and initialisation code is invoked. In C++ this initialisation code is called the constructor and you're free to optionally provide your own constructors if the trivial defaultly provided ones don't suffice.

So even as a language feature, initialisation is part of the construction process. By providing a separate init function you're essentially cheating the language and also allowing your class instance to exist in some invalid or useless state.

Quote:
 We're making a random maze generator. I have a generic Maze class. It has a constructor which initializes it to a maze of the given dimensions where all possible places are walls.
Personally I would imagine such a constructed maze to be an 'empty' maze, one without any walls that is ready to be populated with them, rather than an already 'full' maze.

Quote:
 I then have a static member function of Maze CreateRandomPerfectMaze(), which creates a new Maze, and then sets its walls so that it is a random, perfect maze.
The premise sounds good, but I don't see the use of making it a static member function unless it's accessing non-public static variables? Otherwise do yourself a favour by making it a non-member function to increase encapsulation.

Quote:
 In this example, what is the construction of the Maze? Is it the c'tor of Maze or the CreateRandomPerfectMaze()? Do you consider the initialization and construction of the maze to be separate or together?
The constructor itself initialises a maze to a valid, useable and useful state, even if that state is considered empty (or full in your case).

Your function constructs such a maze, then it populates it with new walls (termed mutation or frobnication) which simply changes it from an already valid state to some other valid state.

What then happens is that this populated maze is returned and used for the copy construction of a second maze object; the copy constructor will initialise this second object to the valid, useable and useful state of the returned maze.

In short: There are two maze objects being constructed, their constuctors both handle initialisation to a valid state and the CreateRandomPerfectMaze function also does some mutation on that state.

So you haven't separated construction from initialisation; furthermore this is such a common pattern that the compiler can perform certain return value optimisations which would be harder or impossible had you handled initialisation separately.

Quote:
 Another more generic example: I've got a class and I want to load its state from an XML file. I call a default c'tor which puts it in a well-defined, initialized, but largely useless state, then I read in the XML file and set the class's properties appropriately. Same questions as above.
It depends what you mean by 'useless'; if the state legitimately has no use then there is no reason for you to ever invoke the constructor that initialises to that state in the first place - it's just like any other function, if you had a function that produced a meaningless result then why would you ever wish to call it? Why would you ever even write such a function?

The natural thing to do would be to load the state from the XML file and then create the object. Or have the object's constructor load it's own state from the file using some kind of abstraction (you probably wouldn't want the object to deal directly with the file).

[Edited by - dmatter on August 16, 2008 10:36:25 AM]

Share on other sites
Thanks for the clarifications, dmatter.

Quote:
Original post by dmatter
Quote:
 We're making a random maze generator. I have a generic Maze class. It has a constructor which initializes it to a maze of the given dimensions where all possible places are walls.

Personally I would imagine such a constructed maze to be an 'empty' maze, one without any walls that is ready to be populated with them, rather than an already 'full' maze.

Sure, I just said already full, since the method I have worked with in the past to generate random, perfect mazes worked by removing walls rather than adding them.

Quote:
Quote:
 I then have a static member function of Maze CreateRandomPerfectMaze(), which creates a new Maze, and then sets its walls so that it is a random, perfect maze.

The premise sounds good, but I don't see the use of making it a static member function unless it's accessing non-public static variables? Otherwise do yourself a favour by making it a non-member function to increase encapsulation.

Good suggestion, though this was only example.

Quote:
Quote:
 In this example, what is the construction of the Maze? Is it the c'tor of Maze or the CreateRandomPerfectMaze()? Do you consider the initialization and construction of the maze to be separate or together?

The constructor itself initialises a maze to a valid, useable and useful state, even if that state is considered empty (or full in your case).

Your function constructs such a maze, then it populates it with new walls (termed mutation or frobnication) which simply changes it from an already valid state to some other valid state.

What then happens is that this populated maze is returned and used for the copy construction of a second maze object; the copy constructor will initialise this second object to the valid, useable and useful state of the returned maze.

In short: There are two maze objects being constructed, their constuctors both handle initialisation to a valid state and the CreateRandomPerfectMaze function also does some mutation on that state.

I'm afraid I don't understand where the copy constructor comes in. If CreateRandomPerfectMaze returns a pointer or reference to what it has created, then surely there's no need for a copy constructor?

Quote:
Quote:
 Another more generic example: I've got a class and I want to load its state from an XML file. I call a default c'tor which puts it in a well-defined, initialized, but largely useless state, then I read in the XML file and set the class's properties appropriately. Same questions as above.
It depends what you mean by 'useless'; if the state legitimately has no use then there is no reason for you to ever invoke the constructor that initialises to that state in the first place - it's just like any other function, if you had a function that produced a meaningless result then why would you ever wish to call it? Why would you ever even write such a function?

The natural thing to do would be to load the state from the XML file and then create the object. Or have the object's constructor load it's own state from the file using some kind of abstraction (you probably wouldn't want the object to deal directly with the file).

Well, from what I understand, the process I described is idiomatic XML reading. Then again, I am extremely inexperienced with XML. I do remember reading articles on XML that stated that any object you load from XML should have a blank constructor and let you write all their properties (the article was for C#, but I think it applies here too) after the load. But either way, you've cleared up the actual questions I had, so thanks!

Share on other sites

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

Create an account

Register a new account

• Forum Statistics

• Total Topics
628710
• Total Posts
2984334

• 23
• 11
• 10
• 13
• 14