manager madness

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • I'll just explain the way that I do it, it has proven extremely well organized and works quite well.

      I have a central "Application" Class which acts as my main hub, it is the only "Manager" Really in the Global scope, my application entry point literally looks like this

      Source Code

      1. CApplication* g_pApp = CApplication::GetInstance();
      2. int main(int argc, char* argv[])
      3. {
      4. if(!g_pApp->Init())
      5. {
      6. g_pApp->ShutDown();
      7. return 1;
      8. }
      9. int retCode = g_pApp->Begin();
      10. if(!g_pApp->ShutDown())
      11. return 1;
      12. return retCode;
      13. }
      Display All


      The Application Class holds any specific Application Managers, like logging, resource cache, string management, etc. as simple pointers. As far as there ordering go's, I don't initialize anything either then setting the default value for pointers and variables in a constructor initialization list, this way I can 'new' all the pointers, regardless of how they should be created, and then initialize them in the proper order with an Init function.

      My Game Logic is also contained in my Application Class, and is created and initialized after everything else, the Game Logic holds it's respective managers, and it also holds a list of Game Views.

      This looks really nice, and if there is one thing that is a necessity for me it's looking at clean code. One tip I can give, is there is alot of redundant commenting, for instance

      Source Code

      1. //Camera
      2. static Camera* spCamera;
      3. //TileManager
      4. static TileManager* spTileManager;
      5. //BitmapManager
      6. static BitmapManager* spBitmapManager;
      7. //ActorManager
      8. static ActorManager* spActorManager;
      9. //KeyboardHandler
      10. static KeyboardHandler* spKeyboardHandler;
      11. //EventManager
      12. static EventManager* spEventManager;
      13. //Collision Manager
      14. static CollisionManager* spCollisionManager;
      Display All


      The variable names themselves are dead giveaway's as to what they do, no need to comment these, getting rid of the comments will clean up alot of space and I'm sure you will like the results

      Source Code

      1. static ActorManager* spActorManager;
      2. static BitmapManager* spBitmapManager;
      3. static Camera* spCamera;
      4. static CollisionManager* spCollisionManager;
      5. static EventManager* spEventManager;
      6. static KeyboardHandler* spKeyboardHandler;
      7. static TileManager* spTileManager;


      I also alphabetize and group all members and methods, it may seem a bit much, but it is one more step that makes your code look really nice, I separate each variable type, than alphabetize, for user defined classes I usually just list them alphabetically like above
      PC - Custom Built
      CPU: 3rd Gen. Intel i7 3770 3.4Ghz
      GPU: ATI Radeon HD 7959 3GB
      RAM: 16GB

      Laptop - Alienware M17x
      CPU: 3rd Gen. Intel i7 - Ivy Bridge
      GPU: NVIDIA GeForce GTX 660M - 2GB GDDR5
      RAM: 8GB Dual Channel DDR3 @ 1600mhz
    • That's the thing, Mike says right in the book, he is going to do things the way that he does it, which makes sense, but there are alot of different ways to skin a cat right?

      I find if I try to copy the exact technique of someone else, not only is it more confusing, but it does not reflect your own style. Some guys who started in plain C show me some of there code, and I honestly can't dig through it, I am too used to my own way of doing things, however, if they explain what needs to be done, designing a program to do the very same thing is made easier for me in my own style.

      I would encourage you to learn from the good coding practices and structures that other more experienced coders use, but don't try to copy them to the letter, try to develop your own style.

      Answering your question though, each manager is different, for instance, even though the logger exists as a part of the Application Class, you don't want to have to do this every time

      Source Code

      1. CApplication::GetInstance()->GetLogger()->LogMessage("Hello");


      For me, the logger is a Singleton. This allows me to Log messages wherever the Logger.h header is included, I make this even simpler by using a Macro to send a message to the singleton, you could use static global functions as well, but using a macro lets you simplify using _LINE_ and _FILE_ .

      Back to the pointer though, the lifetime of the logger is managed by a pointer to it in the CApplication, but is restricted to a single instance as a singleton.

      Not every class needs to be a singleton, or even should be, but for some things it is nice, I actually use a templated Singleton class which helps clean up your classes a bit more as well (If you want I can share it with you), this eliminates having redundant static pointers and GetInstance Methods in your class.

      Here is a look at a Simple Application Class I just wrote up

      C Source Code

      1. #ifndef _CAPPLICATION_H_
      2. #define _CAPPLICATION_H_
      3. //Local
      4. #include "main.h"
      5. #include "../Helper Classes/Singleton.h"
      6. /*INCLUDE SOME OTHER MANAGERS HERE*/
      7. //SDL
      8. #include <SDL/SDL.h>
      9. /*================================
      10. Name: CApplication
      11. Description:
      12. ==================================*/
      13. class CApplication : public Singleton<CApplication>
      14. {
      15. friend class Singleton<CApplication>;
      16. CApplication();
      17. public:
      18. ~CApplication();
      19. bool Init();
      20. bool InitOpenGL();
      21. bool ShutDown();
      22. int Begin();
      23. void Update();
      24. void Render();
      25. //Sub-System Retrieval
      26. CTimer* const GetTimer();
      27. private:
      28. SDL_Surface* m_pScreenSurface;
      29. unsigned int m_nScreenWidth, m_nScreenHeight, m_nScreenBpp;
      30. bool m_bQuit;
      31. //Sub-Systems
      32. CLogger* m_pLogger;
      33. CResourceCache* m_pResCache;
      34. CTimer* m_pTimer;
      35. };
      36. #endif
      Display All


      Notice how I have a Get function for the Timer, you may or may not want your timer to be a singleton, people actually bad mouth singletons alot (people I know at least) , but there explanation is too technical for me so as they are convenient I use them. But here I did not as an example, if you're not using a singleton then use a 'Getter' method to grab the pointer, you could make the pointer constant as well to make sure it is not deleted by accident.

      Also, the indenting is not very clean as the forum moves it around a bit, I use indenting to really help separate code and also to signify scope, so any time I open a nest, I add one more indentation, and when I close it, I return to the previous indentation

      Source Code

      1. class Test
      2. {
      3. public:
      4. Test();
      5. ~Test();
      6. bool Init();
      7. bool ShutDown();
      8. private:
      9. int m_nScreenWidth;
      10. };
      11. bool Test::Init()
      12. {
      13. if(!SomeValue)
      14. {
      15. DoSomething();
      16. }
      17. return true;
      18. }
      Display All
      PC - Custom Built
      CPU: 3rd Gen. Intel i7 3770 3.4Ghz
      GPU: ATI Radeon HD 7959 3GB
      RAM: 16GB

      Laptop - Alienware M17x
      CPU: 3rd Gen. Intel i7 - Ivy Bridge
      GPU: NVIDIA GeForce GTX 660M - 2GB GDDR5
      RAM: 8GB Dual Channel DDR3 @ 1600mhz
    • We do something very similar on The Sims. There's a global module called services where all the various managers live. Having this central location gives you much finer control over the construction and destruction order, as well as allowing you to easily access the managers you want without having to do all those externs. In my own engine, I pretty much do the same thing. I think the concept is a good one.

      Also, never ever have public variables if you can help it. Always use getters and setters. In my entire game engine (which is tens of thousands of lines), I have zero public variables. There are two reasons. First, the pointer itself should be enforced to be read-only. If you expose the public member, anyone can overwrite the pointer. If you have only a getter and no setter, then no one can overwrite it except inside the class. Second, the underlying structure might change. In that case, it's easier to have a single getter you can fix. For example, maybe you no longer have a singleton and instead you want a set of managers for a particular thing. It's an easy fix to just change the getter.

      One last thing, just having straight-up getters is not always what you really want. For example, I have a Scene class that handles all the rendering. The object lives in the application class but there is no GetScene() function. Instead, I have a handful of functions that just pass through to the Scene object. GetViewport() is one example, which returns a Rect of the camera bounds. This decouples the Scene class completely from anyone who doesn't need to know about it. My Logic system needs to know where the camera is, so it can call this function and still have no idea that its talking to the Scene. In fact, I can move the camera implementation anywhere I want with minimal effort because the public interface stays the same.

      Systems that are coupled to the Scene object (like the World system, which needs to render) are given a pointer directly to it. This has worked really well for me.

      -Rez
    • Yes, a singleton pattern on the first GetInstance call will new the class, where sub-sequent calls will simply return a pointer, the same way your manager class GetInstance works. I find it a useful way to eliminate passing pointers through classes.

      Don't get me wrong I didn't mean to say not to use there solutions, but to understand them rather than just type them out, that way you can do the same thing off the top of your head, maybe not looking the same, but at least not having to go back to the book to find the exact way they did it.

      I would highly recommend separating your GetInstance and Init methods, this way you can handle failed initializations, for some things it should be fatal and log any errors and shut down. Believe me it will be alot nicer than having to use exceptions.


      If I'm understanding correctly, you initialize your pointers using GetInstance() to save typing:


      No, the g_pApp is not used for anything either than the Application entry point, it is only referenced between the main braces.

      I use GetInstance for certain things to grab a pointer rather than having to pass the pointer or store one in every class that needs it. Some places where it is used alot it may be stored in a local pointer for convenience

      Source Code

      1. CApplication* pApp = CApplication::GetInstance();
      2. pApp->DoSomething();
      3. pApp->DoSomething();
      4. pApp->DoAnotherThing();


      For instance if I need to grab the timer for movement or something, I will do

      Source Code

      1. CTimer* pTimer = CApplication::GetInstance()->GetTimer();
      2. float deltaTime = pTimer->GetDeltaTime();
      3. Move(vel * deltaTime);
      PC - Custom Built
      CPU: 3rd Gen. Intel i7 3770 3.4Ghz
      GPU: ATI Radeon HD 7959 3GB
      RAM: 16GB

      Laptop - Alienware M17x
      CPU: 3rd Gen. Intel i7 - Ivy Bridge
      GPU: NVIDIA GeForce GTX 660M - 2GB GDDR5
      RAM: 8GB Dual Channel DDR3 @ 1600mhz
    • Originally posted by kaykry
      The lifetime of my Manager class begins the first time GetInstance() is called and ends at program termination. Well, technically I guess it ends after program termination since I register a CleanUp function with atexit():

      Brainfuck Source Code

      1. //--------------------
      2. // Manager::Manager()
      3. //--------------------
      4. Manager::Manager()
      5. {
      6. atexit(CleanUp);
      7. }
      8. //------------------------------
      9. // Manager::CleanUp()
      10. //------------------------------
      11. void Manager::CleanUp()
      12. {
      13. delete spManager;
      14. spManager = NULL;
      15. }
      Display All



      This is not a good pattern to follow. I've had atexit() fail to call my cleanup functions so you can't always rely on it. It's also just generally a really bad pattern.

      In my own engine, I just have an Init() function that's called which initializes things in the order I want. I destroy those things in reverse order in the destructor. This is much cleaner and guaranteed to get run as long as my manager object is destroyed.

      I also don't like the pattern of GetInstance() instantiating the object. This creates an ambiguous initialization order and couples every single call to GetInstance() with an if check, since GetInstance() has to test to see if the object exists. I would much rather explicitly create all of the various objects and have GetInstance() be a simple return. This also gives me one place to handle the failure of any of my major manager classes, which would be fatal error.


      I try to avoid using public variables but I have abused class friends a bit more than I should have in the past. I am still using them on this project. For example, my Manager/Services singleton needs the hwnd value to create the render target. This is a private class member in my App object, so I made the Manager/Services class a friend. A GetHWND() seemed like overkill since it's the only other class that needs it, but I don't know.

      That's the perfect place to have a GetHwnd(). That's actually the purpose of getters. With a friend class, you have full read/write access to every variable on the class. With a simple getter, you don't allow any writes to the HWND. Even if only one other class is calling it, you really should have this be an accessor. Friends are good for allowing a class to be fully invasive. One classic place to use a friend is with a factory class. The factory can be a friend of the class it instantiates, allowing the factory to call private functions that are meant to serve as the interface for creation.

      -Rez