Good/Generic C++ Programming

Started by
41 comments, last by swinchen 17 years, 3 months ago
Hello all, For the impatient here are the questions: I recently bought Design Patterns by the gang of four, and was just wondering what patterns I should pay close attention to in game programming? Are there patterns that show up in game development that are no listed? Are there certain patterns that I should try to avoid? What references do you suggest for C++ design, including advanced C++ usage such a template meta programming? What references do you suggest for good STL usage? Now for a little back ground: I am fairly comfortable with C++ syntax, and I have some decent reference material for when I get stuck. Admittedly my STL knowledge is quite limited... but I feel like I have a decent grasp on the language. I have quickly learned that knowing the syntax does know make you effective at using the language. A good analogy would be this: I have spent a lot of time learning to use individual tools from a work shop... hammers, saws, drills, etc. And I am comfortable with each tool. Now I am dropped in the middle of a construction site and am told I should build a house. I do not know how to effectively use the tools to form a final product. In C++ here is what I am trying to do: I am trying to design a CTextureManager... I am sure that if I could figure out generic programming it would be a CResourceManager. Each image can have multiple textures and I am trying to find a good way to store, search, describe the texture system. I am not sure I explained this real well, but it is the fuel for this entire post. If you have any insight, it would be greatly appreciated! Thanks!
Advertisement
Oh yeah, I should also mention that I learn quickly from example... so if you know of any code fragments (or entire open source projects) that demonstrate a good usage of C++ that would wonderful! Thanks.
ok before i say anything id like to point out that im in a similar situation and that im still not very confident in the whole design area, hence, what i say may be complete crap.

But with that considered heres what i came up with for the whole TextureManager thing in my current project.

TextureManager
// Ive found that templating like this can be extremly useful for testing and // debuggingtemplate<class Texture = Texture, class Resource = ResourceManager<>::Resource>class TextureManager{    // just to save typing :)    typedef ResourceManager<Resource> ResourceManager;public:    // SmrtPtr and WeakPtr typedefs common to all my classes    typedef boost::shared_ptr<TextureManager<Texture, Resource> > SmrtPtr;    typedef boost::weak_ptr<TextureManager<Texture, Resource> > WeakPtr;    // I use TextureManager::Texture so that if i change texture class i only    // need to change the decleration of TextureManager variables and no other    // code    typedef Texture Texture;    // TextureID's should probally be a separete type so...    typedef ResourceManager::ResourceID TextureID;    // My texture manager class internally uses the resource manager to load     // textures    TextureManager(ResourceManager::SmrtPtr resourceManager);    // This should be forwarded to a ResourceManager most likely    TextureID GetTextureID(const std::string& textureName);    // Getting the raw data should be delegated to ResourceManager    // but creating the texture is the job of the texture manager    Texture::SmrtPtr GetTexture(TextureID texture);private:    // well thats up to you :), you can cache textures that have already been    // loaded or whatever you want here.};


ResourceManager
template<class Resource = Resource>class ResourceManager{public:    // SmrtPtr and WeakPtr typedefs common to all my classes    typedef boost::shared_ptr<ResourceManager<Resource> > SmrtPtr;    typedef boost::weak_ptr<ResourceManager<Resource> > WeakPtr;    // same reason as Texture typedef in TextureManager    typedef Resource Resource;    // ResourceID typedef can easily be changed later    typedef int ResourceID;    // ResourceManager loads a list of resources from a file    // the file basically specifies a series of aliases for each resource    // and the actual filename where the resource is stored, ResourceID's    // can be generated here or as a resource is requested    Resource(const std::string& resourceFile);    // So additional resources can be loaded and old ones unloaded.    void LoadResourceFile(const std::string& resourceFile);    // If any resources from the resource file still have external references    // then this should throw an exception    void UnloadResourceFile(const std::string& resourceFile);    // Gets the ResourceID for a particular resource aliase    // This may also generate the ID depending how its implemented    ResourceID GetResourceID(const std::string& resourceName);    // Gets a Resource containing the RAW DATA of that resource.    Resource::SmrtPtr GetResource(ResourceID resourceID);private:    // implementation details :)};


Anyway i hope thats of somehelp, if theres any errors sorry i wrote the code on the fly, but as i said i dont have much experience at the whole design thing so that may be absolute crap.
Quote:Original post by swinchen
Hello all,

For the impatient here are the questions: I recently bought Design Patterns by the gang of four, and was just wondering what patterns I should pay close attention to in game programming? Are there patterns that show up in game development that are no listed? Are there certain patterns that I should try to avoid? What references do you suggest for C++ design, including advanced C++ usage such a template meta programming? What references do you suggest for good STL usage?

For template metaprogramming, I'd suggest "Modern C++ Design" by Andrei Alexandrescu. It's a reference.

About the patter, you shouldpay a close attention to all of them, but you might also want to read different opinion about some controversial patterns (for example, the visitor and the dreaded singleton pattern). At the beginning, you should even try to avoid the singleton pattern as it has a lot of drawbacks and is probably one of the most difficult pattern out there - it's apparent simplicity hides the fact that it is terribly difficult to use.

Quote:Now for a little back ground:

I am fairly comfortable with C++ syntax, and I have some decent reference material for when I get stuck. Admittedly my STL knowledge is quite limited...

You should definitely learn more about the standard C++ library - not only the containers, but the algorithm, the <functional> header and the stream library - because (a) it is part of the language itself (b) It Will Help You.

Quote:but I feel like I have a decent grasp on the language. I have quickly learned that knowing the syntax does know make you effective at using the language. A good analogy would be this: I have spent a lot of time learning to use individual tools from a work shop... hammers, saws, drills, etc. And I am comfortable with each tool. Now I am dropped in the middle of a construction site and am told I should build a house. I do not know how to effectively use the tools to form a final product.

That's a pretty good analogy [smile] I'll reuse it (providing that its interface is clear enough, and that it does not come with additional dependencies).

Quote:In C++ here is what I am trying to do: I am trying to design a CTextureManager... I am sure that if I could figure out generic programming it would be a CResourceManager. Each image can have multiple textures and I am trying to find a good way to store, search, describe the texture system. I am not sure I explained this real well, but it is the fuel for this entire post. If you have any insight, it would be greatly appreciated! Thanks!

A texture manager is not a resource manager. The goal of these entities are very different - not to mention that a resource manager does a lot more than a texture manager. They are separate entities (think: their behavior are different; to subtype a type, you'll want to make sure that their behavior is identical from a caller point of view. That makes core reuse inheritance unaccaptable, so you'll want to use the composition pattern instead, like Julian90 did (r++ BTW)).

Regards,
Quote:Original post by swinchen
I recently bought Design Patterns by the gang of four

well-spent money.

Quote:
and was just wondering what patterns I should pay close attention to in game programming?

Factory, Command, Template, State

A good grasp of eventing is also recommended, though it is not considered a design pattern iirc.

Quote:
Are there certain patterns that I should try to avoid?

Singleton

Quote:
I have quickly learned that knowing the syntax does know make you effective at using the language. A good analogy would be this:

You are a wise man. Nice analogy!
Quote:Original post by Emmanuel Deloget
For template metaprogramming, I'd suggest "Modern C++ Design" by Andrei Alexandrescu. It's a reference.


Better add C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond after because Andrei's book is slighly dated and lacks some other useful techniques as well as how one reduces compile-times and code bloat when doing template metaprogramming.
In my current project i use the singleton pattern quite extensivly.
All my managers use it, because i need references to them in all sorts of places in my code.

// some usage examplesOVGeometryManager::getSingleton().loadFile(...);OVShaderManager::getSingleton().loadFile(...);OVRenderer::getSingleton().update(...);// The managers are all instantiated in the renderer so a call tom_Renderer->getGeometryManager() ...m_Renderer->getShaderManager() ...// would do the same


The convinience of singletons is, that you include the header-file of the manager to use and get it with a call to e.g. OVRenderer::getSingleton() anywhere in your code, without passing or storing a pointer or reference to the manager.

Another example:
My design of shaders (in my context shaders are material descriptions like in offline renderers) allow to implement their own rendering loop and therefore i need a reference to my renderinterface and hence i would need to pass a reference or pointer as an argument to the shader constructor. With singletons i can skip this step.


In my current state of knowledge the benefits of singletons outweigh their restrictions.
I am just starting programming and i know that i know nothing and therefore i would like to hear some opinions of why to avoid singleton patterns ;)


For some interested in the usage and implementation of singletons:
(currently using Linux, gcc4.03)

Full credit goes to the guys of the Ogre3D project http://www.ogre3d.org

Header of OVGeometryManager
#include "OVSingleton.hh"class OVGeometryManager : public OVSingleton<OVGeometryManager>{public:	static OVGeometryManager& getSingleton( void );	static OVGeometryManager* getSingletonPtr( void );...};


Body of OVGeometryManager
#include "OVgeometrymanager.hh"template<> OVGeometryManager* OVSingleton<OVGeometryManager>::ms_Singleton_p = NULL;OVGeometryManager& OVGeometryManager::getSingleton( void ){    assert(ms_Singleton_p);    return (*ms_Singleton_p);}OVGeometryManager* OVGeometryManager::getSingletonPtr( void ){    return ms_Singleton_p;}...


Header of OVSingleton
#ifndef OVSINGLETON_HH#define OVSINGLETON_HH#include <assert.h>namespace OpenVision{	template<typename T>	class OVSingleton	{	public:		OVSingleton( void )		{			assert(!ms_Singleton_p);			ms_Singleton_p = static_cast<T*>(this);		}		virtual ~OVSingleton( void )		{			ms_Singleton_p = 0;		}		static T& getSingleton( void )		{			assert(ms_Singleton_p);			return (*ms_Singleton_p);		}		static T* getSingletonPtr( void )		{			return ms_Singleton_p;		}	protected:		static T* ms_Singleton_p;	};} // end of namespace OpenVision#endif
Quote:Original post by hoLogramm
In my current project i use the singleton pattern quite extensivly.
All my managers use it, because i need references to them in all sorts of places in my code.

If you do, you have a serious design problem. Refactor.
"All sorts of places" should not need access to these things.

Quote:
The convinience of singletons is, that you include the header-file of the manager to use and get it with a call to e.g. OVRenderer::getSingleton() anywhere in your code, without passing or storing a pointer or reference to the manager.

The convenience of robbing a bank is that I get a lot of money here and now. That doesn't make it a good long-term strategy. [wink]

Quote:
My design of shaders (in my context shaders are material descriptions like in offline renderers) allow to implement their own rendering loop and therefore i need a reference to my renderinterface and hence i would need to pass a reference or pointer as an argument to the shader constructor. With singletons i can skip this step.

Yes, but why is it a good thing to skip that step?

Quote:
I am just starting programming and i know that i know nothing and therefore i would like to hear some opinions of why to avoid singleton patterns ;)

Basically because even though they're convenient, they make a mess of your code. You said it yourself. All sorts of places in your code need access to various managers. That shouldn't be the case, but it happens very easily when you use singletons. Normally, you try to *avoid* globals, because then you can control how and when resources are accessed, you can refactor much more easily, and you can actually *see* which resources are required for any given function, because it doesn't pull in random global variables.

That's the short version... I'm sure others will be happy to provide the long, advanced explanation [grin]
Quote:Original post by hoLogramm
In my current project i use the singleton pattern quite extensivly.
All my managers use it, because i need references to them in all sorts of places in my code.

Frankly, you shouldn't. You transformed the singleton pattern in a OOified global, which is not its goal at all. At this step, you should read Washu's Singletonitis articles (my journal has the links to the top; You can also go directly to Washu's journal - the 3 parts of the article were written in April).

Quote:Original post by hoLogramm
The convinience of singletons is, that you include the header-file of the manager to use and get it with a call to e.g. OVRenderer::getSingleton() anywhere in your code, without passing or storing a pointer or reference to the manager.

But in this case, there's a huge drawback to the convenience: all your code is now dependant upon your OVrenderer, which is no longer used locally. I can think of two reason about this
1) your OVrenderer class is a god class, meaning that it probably violate the single responsability principle.
2) the OVrenderer class is used locally (in in a limited subset of classes) but the classes that are using it are deeply trenched in the code (either because of a deep class hierarchy or because of an overmisuse of the composition pattern). Such code smells, but it's going to be harder to see where the real problem lies without studying it (to get a proper feeling about this strange smell thing, here are some code smell metrics to help you).

Quote:Another example:
My design of shaders (in my context shaders are material descriptions like in offline renderers) allow to implement their own rendering loop and therefore i need a reference to my renderinterface and hence i would need to pass a reference or pointer as an argument to the shader constructor. With singletons i can skip this step.

Just tell me that you don't use a singleton just to bypass this step [grin]. More seriously, your statement tells me that you are probably in case 1 above. Your renderer seems to do papa and mama, and you should consider a refactor based on the different responsabilities that your renderer is currently handling.
Here are, for instance, the methods of my own renderer (DX based):
class renderer{public:  // ctor and dtor - can't do much without them  bool tie_resource(resource *res);  void clear();  bool begin_scene();  void end_scene();    // there are multiple overloads of the draw method.  // none of them takes a resource object as a parameter  // resources are bound to the device using resource_type::bind()  // where resource_type inherits resource (resource does not define  // a bind() methods, since my resources are heavily generalized and  // not all of them are bindable. I may explain this later, when I'll  // get more free time.   bool draw(/* params */);};

Needless to say, my neither my resources nor anything depends on this renderer class.

As you can see, the sole responsability is to handle the draw operations. Setup and state are handled outside the renderer - remember that the fact that the renderer references a IDirect3DDevice9 instance does not mean that no other object can reference the same instance.

I forgot some methods, but none of them are very important wrt the design.

The only exception are these xxx_resource() functions. tie_resource() ties a particular resource with this renderer and allow its creation in the context of this renderer (in return, tie_resource() calls a private virtual method of resource).

Quote:In my current state of knowledge the benefits of singletons outweigh their restrictions.
I am just starting programming and i know that i know nothing and therefore i would like to hear some opinions of why to avoid singleton patterns ;)

That's one of the reason why there are people here that tries to give you meaningfull information about different pattern and techniques - to help you to see the pro and cons of these patterns/techniques (ie. see Wahsu's articles [smile])

Quote:For some interested in the usage and implementation of singletons:
(currently using Linux, gcc4.03)

Full credit goes to the guys of the Ogre3D project http://www.ogre3d.org

Yeeeeeks!

I had an heart attack [smile].

The Ogre singleton is probably the worst kind of C++ singleton you'll find out there. One reason lies in the way it is created: the user have to create it manually. Another reason lies in the fact that C++ enables you to create a singleton whose uniqueness is enforced at compile time (reducing the risk of errors), while the uniqueness of the Ogre singleton is enforced at run time (meaning that you'll have to handle error cases by yourself, resulting in potentially clumsy code). Another reason lies in the fact that:
OVSingleton<OVRenderer> *renderer2 = new OVSingleton<OVRenderer>(OVRenderer::getSingleton());

will create another instance of your singleton - woops. The use of the copy constructor (automatically generated by the compiler if you don't provide it) kills everything.

I provide a safer singleton in my journal. You might want to use that instead (at least, you'll get rid of some of the problems of the Ogre "singleton" (I'm not even sure they should call that... thing a singleton [wink])

I don't say that singletons are bad. Their problem is that they appear to be very easy to use, but most of the time they are badly abused.
I am really sorry swinchen to take over your thread topic, but since it is about design patterns i am very interested in it :)

To Emmanuel Deloget:
Your post slipped in while i wrote this one.
Very big thanks of your resources and thoughts. ;) ++rating
I explained my renderer a little bit further down in this reply but it looks as if we have the same design. (creating-un/binding resources, render(), postRender(), renderGeometry(), ...)
I am very interested in your singleton implementation. I'll check you journal now regularly. :)

Quote:Original post by Spoonbender
Quote:
My design of shaders (in my context shaders are material descriptions like in offline renderers) allow to implement their own rendering loop and therefore i need a reference to my renderinterface and hence i would need to pass a reference or pointer as an argument to the shader constructor. With singletons i can skip this step.

Yes, but why is it a good thing to skip that step?

Well if i did not use singletons, i would have additional parameters in my constructors and forward declarations and pointers in my header files for every manager i am going to use. With singletons i have the header of my manager included in my cpp and nothing else. I hope i explained why in my point of view it is a good step to skip this part.

Quote:Original post by Spoonbender
Quote:
I am just starting programming and i know that i know nothing and therefore i would like to hear some opinions of why to avoid singleton patterns ;)

Basically because even though they're convenient, they make a mess of your code. You said it yourself. All sorts of places in your code need access to various managers. That shouldn't be the case, but it happens very easily when you use singletons. Normally, you try to *avoid* globals, because then you can control how and when resources are accessed, you can refactor much more easily, and you can actually *see* which resources are required for any given function, because it doesn't pull in random global variables.

That's the short version... I'm sure others will be happy to provide the long, advanced explanation [grin]

Thanks for the explanation ;)
I am going to look for the long story ;)


I see this is going about design philosophies.
My own is that the application tells the renderer what scene it should render, the renderer then goes through all shaders and tells them to render themselves. The shader uses the renderer then to bind states, textures, framebuffer objects,... An easy task, because the shader knows all objects, which it is attached to (in a prestep the scene gathers all shaders it should use according to all visible objects and stores pointers to those in the shader directly) and i don't have to sort them by their textures, materials, hardware shaders, ... So the renderer delegates instead of fiddeling around with multiple pass-, multiple viewport rendering and different techniques (like using hardware shaders or the fixed function pipeline and so on ... it only binds and unbinds them).
In addition all objects have their own render function, so they know how to setup themselves.


For the global thing i did not quite get it, but the long story surely will help me to understand it.
With *all sorts of places* i meant where they are _needed and nowhere else_, since they are included only in .cpp's.
And a superior benefit of the singleton implementation over pointers is, that they can't be changed and ensure, that there only will be one instance of a certain type of e.g. manager around.
As long as no one named "Emmanuel Deloget" comes around and plays with the copy constructor. :D

[Edited by - hoLogramm on December 19, 2006 8:54:25 AM]

This topic is closed to new replies.

Advertisement