Thread: Final resource mgr design

  1. #1
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607

    Final resource mgr design

    After using my extremely flawed resource manager I finally re-designed it.

    Here is the new design.

    BaseMgr
    -----------
    - A template mgr class that wraps a map<unsigned int, T* pObject>.
    - Supports Add, Remove, Get, and Update.
    - Cleans up memory during destruction

    DerivedMgr
    --------------
    - Simply derive from manager base and set the template type.


    ResMgr
    ---------
    - Class that wraps a vector of BaseMgr objects - ID is returned to user when
    object is added - unique ID which is just the size_t index of the object
    - Supports Add, Remove, Get, and Update
    - Cleans up memory during destruction


    Nice and simple and handles all my resources. It would be easy to add in a cache to disk scheme in the update function of BaseMgr.

    Comments? I did it this way b/c most of the manager classes shared the same traits and basic operations. Coding those for every type of manager became really annoying. And now I have a nice way of wrapping all the manager's into one object called a resource manager. The main app class has a pointer to this which can then be used to access the manager's it contains.

    The only problem now is the size_t index of the manager's in the ResMgr vector. This would not be known to other objects and the only scheme I can come up with is an enum that these objects can use to access the manager objects. I don't like this but haven't figured out a better method yet.
    Last edited by VirtualAce; 07-08-2008 at 11:14 PM.

  2. #2
    Woof, woof! zacs7's Avatar
    Join Date
    Mar 2007
    Location
    Australia
    Posts
    3,459
    Great idea, how are you storing resources on disk though? Native directory / file structure or a sortof PAK approach? And any reason why you chose to abstract the BaseMgr from the ResMgr? Rather than one fat lump?

  3. #3
    Registered User
    Join Date
    Jan 2006
    Location
    Sweden
    Posts
    92
    When I wrote a game state manager some time ago I had a similar problem; I didn't want to return a direct index to the game state as I would need something to keep track of all those indicies and I wasn't keen on using an enumeration either.
    The way I solved it was to use a string as identifier in a map, each game state was registered with a string and that string was used to get and set the game state. I still like this solution as it was well suited for my purpose.

    Example (I don't have the code at the moment, but it was something like this):
    Code:
    //Make the game state available to the manager
    m_pGameStateMgr->RegisterState(new MainMenuGameState(), "MainMenu");
    //could have been this, can't remember:
    m_pGameStateMgr->RegisterState<MainMenuGameState>("MainMenu");
    
    
    //Change the current game state.
    m_pGameStateMgr->SetState("MainMenu");
    
    //Fetch the game state.
    menuState = m_pGameStateMgr->GetState("MainMenu");

    Oh, and I really like your design!


    Daniel

  4. #4

    Join Date
    May 2005
    Posts
    1,042
    I like it because it's straightforward and simple. Your other designs made good progress but they seemed a bit cumbersome to use at times.
    I'm not immature, I'm refined in the opposite direction.

  5. #5
    Registered User
    Join Date
    Nov 2005
    Posts
    673
    That is similar to what my system came out to be. Although my resource mgr is just a static singleton.
    Code:
    class ResourceMgr
    {
     private:
      std::vector< std::map< size_t, boost::any > > resources;
    
      //Garbage collecting functions ( called per-frame to ensure clean resource table )
      //
     public:
      //Other stuff not shown here :)
    
    };
    That is basically what it is. A garbage collecting resource manager seemed like the best way of doing. Also, as you said rewriting the manager for each different type is exhausting.

  6. #6
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Good comments. Guess I'm on the right track then. I'm also designing a windowing system very similar to how Win32 works but much simpler. During update the Window manager determines if any key presses or mouse clicks need to be sent to any of the registered windows. When it does determine that a mouse event or key event needs sent to a window it does so in the form of a message structure. The window manager calls the WndProc for the window and passes the message as a param.

    From then on out it's up to the WndProc of the window to determine what to do with the message. The base class window has many generic functions in it that allow you to set colors, font, title, shadows, style, etc. I'll have to add more generics as I come across different types of windows that will need different functionality.

    The controls I currently want to support are:
    Edit control
    Button control
    List box control
    Combo control
    Tree control
    Tab control

    The styles I currently want to support are:
    Edit
    - multi-line edit

    Button
    - rectangular
    - rounded corners
    - radio
    - check box
    -- 2 and 3 state

    List box
    - single select
    - multi select
    - sorted
    - check boxes

    Combo
    - drop down
    - drop list
    - single select
    - multi-select

    Tree control
    - with/without lines
    - in place label editing
    - auto-expand branches
    - check boxes
    - single select
    - multi-select

    Tab control
    - sorted
    - multi-row
    - vertical tabs (bottom or top)
    - horizontal tabs (left or right)

    I think this will be enough for base functionality. Once I get the system up and running then I'll probably expand it a bit to accomodate for any gaping holes I find. I have to get a UI system up and running b/c a lack of it is really slowing me down.

  7. #7
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Sounds very nice Bubba. Only thing that seems missing is some sort of display list that you can use to implement Z index functionality. Or probably you already have that.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  8. #8
    Woof, woof! zacs7's Avatar
    Join Date
    Mar 2007
    Location
    Australia
    Posts
    3,459
    How about a group control? ie like a frame, or are you going to have a 'child window' or 'everything is a window' concept?

  9. #9
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Not sure yet. Once I get this basic system up and running I'll be able to add other controls. It's very hard to design something like this with 1 person so my control list is probably limited.

    EDIT: Resource mgr classes

    Code:
    template <class T>
        class MgrBase
        {
            public:
                MgrBase();
                virtual ~MgrBase();
            
                virtual size_t Add(T *Object);
                virtual bool Remove(size_t ID);
                virtual void Update(float timeDelta);
                T* Get(size_t ID);     
                size_t getMemSize();
    
                virtual void notifyAdd(size_t ID);
                virtual void notifyRemove(size_t ID);
                           
            protected:
                std::map< size_t,T* > m_Objects;
    
        };
    
        template <class T>
        void MgrBase<T>::notifyAdd(size_t ID)
        {
        }
    
        template <class T>
        void MgrBase<T>::notifyRemove(size_t ID)
        {
        }
    
                
        template <class T> 
        MgrBase<T>::~MgrBase()
        {
            std::map<size_t,T*>::iterator map_iter = m_Objects.begin();
    
            while (map_iter != m_Objects.end())
            {
                delete map_iter->second;
                m_Objects.erase(map_iter);
                ++map_iter;
            }
    
            m_Objects.clear();
        }
    
        template <class T> 
        size_t MgrBase<T>::Add(T *Object)
        {
            m_Objects.insert(std::pair<size_t,T*>(m_Objects.size(),Object));
            notifyAdd(m_Objects.size() - 1);
            
            return m_Objects.size() - 1;
        }
    
        template <class T> 
        bool MgrBase<T>::Remove(size_t ID)
        {
            std::map<size_t,T*>::iterator map_iter = m_Objects.find(ID);
    
            if (map_iter == m_Objects.end())
            {
                return false;
            }
    
            m_Objects.erase(map_iter);
            notifyRemove(ID);
    
            return true;
        }
    
        template <class T> 
        T* MgrBase<T>::Get(size_t ID)
        {
            std::map<size_t,T*>::iterator map_iter = m_Objects.find(ID);
    
            if (map_iter == m_Objects.end())
            {
                return NULL;
            }
    
            return map_iter->second;
        }
    
        template <class T>
        void MgrBase<T>::Update(float timeDelta)
        {
            
        }
    
        template <class T>
        size_t MgrBase<T>::getMemSize()
        {
            return m_Objects.size() * sizeof(T);
        }
    Code:
    class ResMgr
        {
            typedef std::map<size_t,MgrBase<IObject *>*> MgrMap;
            public:
                ResMgr();
                virtual ~ResMgr();
            
                virtual size_t Add(MgrBase<IObject *> *Object);
                virtual bool Remove(size_t ID);
                virtual void Update(float timeDelta);
                MgrBase<IObject *> *Get(size_t ID);  
                size_t getMemSize();
    
            protected:
                MgrMap m_Mgrs;
                size_t m_MemSize;
        };
    
        inline ResMgr::ResMgr():m_MemSize(0)
        {
        }
    
        inline ResMgr::~ResMgr()
        {
            MgrMap::iterator map_iter = m_Mgrs.begin();
    
            while (map_iter != m_Mgrs.end())
            {
                delete map_iter->second;
                m_Mgrs.erase(map_iter);
                ++map_iter;
            }
    
            m_Mgrs.clear();
        }
    
        inline size_t ResMgr::Add(MgrBase<IObject *> *Object)
        {
            m_Mgrs.insert(std::pair<size_t,MgrBase<IObject *>*>(m_Mgrs.size(),Object));
            m_MemSize += Object->getMemSize();
    
            return m_Mgrs.size() - 1;
        }
    
        inline bool ResMgr::Remove(size_t ID)
        {
            MgrMap::iterator map_iter = m_Mgrs.find(ID);
    
            if (map_iter == m_Mgrs.end())
            {
                return false;
            }
    
            
            m_MemSize -= map_iter->second->getMemSize();
            delete map_iter->second;
            m_Mgrs.erase(map_iter);
    
            return true;
        }
    
        inline void ResMgr::Update(float timeDelta)
        {
            MgrMap::iterator map_iter = m_Mgrs.begin();
    
            while (map_iter != m_Mgrs.end())
            {
                map_iter->second->Update(timeDelta);
                ++map_iter;
            }
        }
    
        inline MgrBase<IObject *> *ResMgr::Get(size_t ID)
        {
            MgrMap::iterator map_iter = m_Mgrs.find(ID);
    
            if (map_iter == m_Mgrs.end())
            {
                return 0;
            }
    
            return map_iter->second;
        }
    
        inline unsigned int ResMgr::getMemSize()
        {
            return m_MemSize;
        }
    Last edited by VirtualAce; 07-10-2008 at 11:54 PM.

  10. #10
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    I feel like there should be some easy hook for different cache strategies and the like - a virtual function in the right place, perhaps, or a trait template parameter. Sounds to me like this would be one of the core change points of a resource manager and thus should be considered in the design.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  11. #11
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    This can be handled in the update function.

    I actually changed the initial design a bit and now the only class is MgrBase<T>. I also have a MgrBase<class key, class value> which wraps a map but lets you specify the key and value. This allows for easy mapping later on.

    Essentially most of this boils down to:

    typedef MgrBase<Object> MgrType;

    MgrBase< MgrType > res_mgr;

    But I want to wrap the class so that you can say getMgr(<some_id>). Some_id is a key type that is determine by the user of the class.

  12. #12
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Scrapped the ResMgr class, refactored the MgrBase class and here is the final managers that come as a result of the changes.

    Code:
    typedef x3d_core::MgrBase< std::string, Object > MgrType;
    typedef x3d_core::MgrBase< std::string, MgrType > GameObjMgrType;
        
    GameObjMgrType m_GameObjMgr;
    
    typedef x3d_core::MgrBase< std::string, Resource > ResType;
    typedef x3d_core::MgrBase< std::string, ResType > ResMgrType;
    
    ResMgrType m_ResMgr;
    Now I have a resource manager and a game object manager that both take advantage of the template class.


    Here is an example of how simple this makes managing objects.

    Code:
    #pragma once
    
    #include "MgrBase.h"
    #include "GameObject.h"
    #include <string>
    
    class GameSectorMgr:public x3d_core::MgrBase<std::string,GameObject *>
    {
        public:
            GameSectorMgr(void);
            virtual ~GameSectorMgr(void);
            virtual void Update(float timeDelta);
    };
    Code:
    #include "GameSectorMgr.h"
    
    GameSectorMgr::GameSectorMgr(void)
    {
    }
    
    GameSectorMgr::~GameSectorMgr(void)
    {
    }
    
    void GameSectorMgr::Update(float timeDelta)
    {
        std::map<std::string,GameObject *>::iterator iter(m_Objects.begin());
        std::map<std::string,GameObject *>::iterator end(m_Objects.end());
    
        while (iter != end)
        {
            iter->second->Update(timeDelta);
            ++iter;
        }
    }
    Last edited by VirtualAce; 07-12-2008 at 06:27 PM.

  13. #13
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    typedef x3d_core::MgrBase< std::string, Object > MgrType;
    Shouldn't that be GameObjType to be consistent?
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  14. #14
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Probably but this code isn't for prime time and I'll probably be the only one to ever maintain it.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Sorting out a resource manager
    By psychopath in forum Game Programming
    Replies: 1
    Last Post: 11-10-2008, 07:12 PM
  2. CreateProcess with Resource of executable, not the Filename
    By Ktulu in forum Windows Programming
    Replies: 4
    Last Post: 11-04-2006, 01:07 AM
  3. Generic Resource_Manager WIP with lots TODO
    By Shamino in forum C++ Programming
    Replies: 19
    Last Post: 02-01-2006, 01:55 AM
  4. resource problem/question
    By stallion in forum Windows Programming
    Replies: 4
    Last Post: 01-29-2003, 02:08 PM
  5. Serial Communications in C
    By ExDigit in forum Windows Programming
    Replies: 7
    Last Post: 01-09-2002, 10:52 AM