Sign in to follow this  
Part_E

C++ Organization Assistance Request

Recommended Posts

This board has been such a huge help to me, I was hoping to further that trend with another (Hopefully simple) question:

I'm nearly done with the skeletal framework of my game's openGL-based GUI system, but I'm having trouble organizing a few parts.

To put it into perspective, I have a primary class named CGame, which extends CGUI. Mostly so actions from CGUI can bubble up to CGame and be interpreted and executed.
I also have a TextureManager object that's created as a public member of CGame - It's intended to keep track of and assist in the loading openGL textures.

So CGUI can access the TextureManager object in CGame via these methods I created (Along with corresponding virtual function declarations in CGUI):
[code]

GLuint CGame::LoadTexture( SDL_Surface *TextureImage ) {
return TextureManager.LoadTexture( TextureImage );
}

GLuint CGame::LoadTexture( char *FileName ) {
return TextureManager.LoadTexture( FileName );
}
[/code]
The problem is that I want the actual control objects that CGUI creates and tracks (OTextLabel, OButton, OProgressBar, etc) to be able to access these texture methods. So that when I create a new text label OTextLabel's constructor can request just a string, turn that into rendered text on an SDL_Surface, and then somehow call the LoadTexture method to load that surface as a texture and return the GLuint texture ID to be stored in OTextLabel. But OTextLabel can't see CGUI, or CGame, or TextureManager.

And I can't quite wrap my head around creating accessors for it.

Can anyone help? Let me know if more information is required.

Share this post


Link to post
Share on other sites
Ok, I think I'm just going overboard with object orientation. I realized I don't really need a TextureManager class, since the GLuints it was storing were already being stored in other places.

Now I just have some global functions floating about for LoadTexture() and everything is able to access them. I was just trying to avoid using global functions if at all possible.

Share this post


Link to post
Share on other sites
With my gui's I tend to keep them simple, but they always have a mix of MVC and flyweight designs to them. I keep the graphical and physical representation pretty much separate. Other than the fact that the controls keep a flyweight kind of struct that describes what skinning it will need. I then have a separate viewer that can interpret the gui's data and attempt to apply the skinning to it.

The flyweight would be somthing like.

skinName = "..."
borderWeight = "..."
icon = "..."
label = "..."

That alone can incorporate any kind of control. The skinName would be translated to an id for faster look up at load time, and the rest of the needed information about the graphics would be stored else were. When rendering the viewer would take the gui's physical information like position size, and sub orientations, and using the skins' information apply it to the area.

That way the gui doesn't get cluttered and it's rather easy to allow different skinning techniques without have to rewrite major portion of the actual gui.

To facilitate loading of the graphics, I wouldn't derive or even allow the viewer to own the render-er / texture loader. Instead I would pass the render-er to it as an argument of a function, for when it needs it. Once again this allows it to be decoupled from the render-er.

Share this post


Link to post
Share on other sites
Huh... Well I hadn't thought of passing a reference to the TextureManager object on to the UI control, but that's a viable solution. Wonder why I didn't think of it.

But I am sort of using a flyweight design as it is. There's a universal OControl object which is extended by each of the controls and shares common methods between them all. As it is, each control object has a DrawControl() method to draw itself. So I can do things like:
[code]
OControl *Controls[2];
Controls[0] = new OButton( x, y, ThisTexture, "ThisText" );
Controls[1] = new OProgressBar( x, y, AnotherTexture, Percent );
for( i = 0; i < 2; i++)
Controls[i]->DrawControl();
[/code]
That's simplified, but you get the idea. Is this what you mean by flyweight design?

Share this post


Link to post
Share on other sites
[quote name='Part_E' timestamp='1307393468' post='4820255']
Now I just have some global functions floating about for LoadTexture() and everything is able to access them. I was just trying to avoid using global functions if at all possible.
[/quote]
I wouldn't resign yourself to global functions/objects just yet; as mentioned above, there are fairly straightforward object-oriented solutions to the types of problems you're trying to solve. (Assuming the goal is object-orientation, that is.)

Also, what do the 'C' and 'O' prefixes to your class names stand for?

Share this post


Link to post
Share on other sites
[quote name='jyk' timestamp='1307409785' post='4820326']
[quote name='Part_E' timestamp='1307393468' post='4820255']
Now I just have some global functions floating about for LoadTexture() and everything is able to access them. I was just trying to avoid using global functions if at all possible.
[/quote]
I wouldn't resign yourself to global functions/objects just yet; as mentioned above, there are fairly straightforward object-oriented solutions to the types of problems you're trying to solve. (Assuming the goal is object-orientation, that is.)

Also, what do the 'C' and 'O' prefixes to your class names stand for?
[/quote]

I manged to get it working by passing the TextureManager object by reference to the UI control constructors. But I don't know why I use C and O, really. It's pretty arbitrary, but it helps me. C for classes I'll only be instantiating one of, and O for classes that I'll be creating multiples of.

Share this post


Link to post
Share on other sites
[quote name='Part_E' timestamp='1307404222' post='4820303']
But I am sort of using a flyweight design as it is.
[/quote]

No that's just inheritance, and polymorphism... [url="http://sourcemaking.com/design_patterns/flyweight"]flyweights[/url] can be better thought of as a blue print. or separate set of data that many objects share. They are good for heavy factory generated objects, or objects were there are alot of and they basically all store the same exact information. GUI being a great example, one gui is going to use the same sprites for basic elements on all of it's controls. ie all the buttons are going to use the same images, so you store those separately and use a id to look up the flyweight that has that image. so instead of having 100 buttons with a 100 loaded textures, you only need one. This also saves time with loaded all the data, especially when there are a lot of the objects.

[quote name='Part_E' timestamp='1307422730' post='4820387']
But I don't know why I use C and O, really. ...O for classes that I'll be creating multiples of.
[/quote]

Very interesting... I've too, never seen that used before.

Share this post


Link to post
Share on other sites
[quote name='Part_E' timestamp='1307422730' post='4820387']
But I don't know why I use C and O, really. It's pretty arbitrary, but it helps me. C for classes I'll only be instantiating one of, and O for classes that I'll be creating multiples of.[/quote]
Just for the sake of discussion, here's a few arguments against using such prefixes. (Obviously you can use whatever naming convention you want, but these are just some things to consider.)

Although I imagine name 'warts' of this type still see a fair amount of use, I think there's a trend away from such naming conventions.

One issue is that using unusual conventions can make the code harder for others to understand. (In this particular case, I'd be willing to bet that few if any programmers would be able to guess the meanings of your 'C' and 'O' prefixes if you didn't tell them :)

As for the general arguments against such naming conventions, a forum search for 'hungarian' or 'class c prefix' should lead you to some threads in which said arguments are laid out clearly. Just to throw a few of them out though:

Although someone who's used to the convention might consider the code easier to read, I think it can be argued that in general such conventions make code harder to read in the sense that it doesn't read like you would normally speak or write. The class names Car and House, for example, are clear and easy to read. CCar and CHouse, however, are less so. Your earlier post is a good example of this; the post is cluttered with C's and O's that break up the continuity and provide no useful information to us, the readers.

Another (perhaps better) argument has to do with fluidity and flexibility. One of the arguments you'll often see made against type prefixes is that, for example, if you decide to make m_iTemperature a float instead of an integer, you have to change the variable name throughout your code as well, or, if you forget, you now have information attached to the variable name that's not only superfluous, but incorrect.

Using prefixes to indicate whether you intend to create only one instance or many instances is a perfect example of this, I think. In fact, the argument against it is very similar to one of the key arguments against singletons: why go to extra effort to impose a constraint that you may end up having to remove or change anyway?

For many of the types that people tend to make singletons, such as resource caches, there's no particular reason there should only be one. You might think, 'Oh, I'll only ever need one texture cache'. But then you find you need to load a small set of textures for your loading screen, separate from the bulk of the textures. Well, why not just create a cache specifically for that purpose, and aim it at a directory that only includes the textures needed for the loading screen? If the resource cache class is just a normal class with no artificial 'one only' constraint imposed, this can be easily done.

A similar argument can be applied to the 'C' and 'O' prefix, I think. Using these prefixes suggests that you know, in advance, for every class type, whether you'll only ever need to instantiate one, or if you'll need to instantiate more than one. Why is this an important distinction to make? And what happens if you change your mind about a class? You can do a project-wide search and replace, but why go to all that trouble? And if you forget to make the change, then again you're left with information that is not only superfluous, but incorrect.

Anyway, the above is just my view on it, which you can take or leave. (And, I know some people still argue for the use of these sorts of naming conventions.)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this