Thread: Generic Resource_Manager WIP with lots TODO

  1. #16
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034
    I've grown to love the Singleton pattern, even if its commonly known overusing it is bad. I think the information that a class is a singleton is of no concern to that class, so should be kept seperate (like in a System or Manager or through Boost).

    I modified your code a bit to match my system a little, and attempted to clean it up to what I think would be logical (and as compulsion I've changed a lot of formatting). It doesn't mean its right, and I concertainly wouldn't expect it to be since I had some troubles making out the entire system, but maybe it'll give you a different perspective - who knows.

    Code:
    template< typename tnResource >
    class ResourceContainer
    {
    public:
    	bool CheckExists(const std::string&);
    	tnResource* Find(const std::string&);
    	void Add(const std::string&, tnResource*);
    	void Remove(const std::string&, tnResource*) {}
    
    	virtual ~Manager(void)
    	{
    		std::map< std::string, tnResource* >::const_iterator I;
    		for(I = m_Resources.begin(); I != m_Resources.end(); ++I) 
    		{
    			delete I->second;
    		}
    	}
    
    private:
    	std::map< std::string, tnResource* > m_Resources;
    };
    
    template< typename tnResource >
    tnResource* ResourceContainer< tnResource >::Find(const std::string& p_sFilename) 
    {
    	std::map< std::string, tnResource* >::const_iterator entry = m_Resources.find(p_sFilename);
    
    	if(entry != Resources.end()) // if the entry is found
    		return entry->second; // return the reference to the resource
    	else
    		return false;
    }
    
    template< typename tnResource >
    bool ResourceContainer<tnResource>::CheckExists(const std::string& p_sFilename)
    {
    	std::map< std::string, tnResource* >::const_iterator entry = m_Resources.find(p_sFilename);
    
    	if(entry != m_Resources.end()) // if the entry is found
    		return true;
    	else
    		return false;
    }
    
    template< typename tnResource >
    void ResourceContainer< tnResource >::Add(const std::string& p_sFilename, tnResource* Resource)
    {
    	m_Resources.insert(std::make_pair(p_sFilename, Resource));
    }
    
    class Manager // singleton god
    {
    public:
    
    	ResourceManager* Resources()
    	{
    		if(!m_ResourceManager)
    			m_ResourceManager = new ResourceManager();
    
    		return m_ResourceManager;
    	}
    
    	D3DTextureHandler* Textures()
    	{
    		if(!m_D3DTextureHandler)
    			m_D3DTextureHandler = new D3DTextureHandler();
    
    		return m_D3DTextureHandler;
    	}
    
    	Manager* Instance()
    	{
    		if(!m_Instance)
    			m_Instance = new Manager();
    
    		return m_Instance;
    	}
    
    protected:
    	Manager()
    	{
    
    	}
    
    private:
    	Manager* m_Instance;
    
    	ResourceManager* m_ResourceManager;
    	D3DTextureHandler* m_D3DTextureHandler;
    };
    
    class ResourceManager
    {
    public:
    	void Add(std::string p_sFilename)
    	{
    		switch(GetExtension(p_sFilename).c_string())
    		{
    			case "ms3d": m_MS3DModelManager->Create(p_sFilename);
    			break;
    			case "ai": //m_AIMotionManager->Create(p_sFilename);
    			break;
    			default:
    			break;
    		}
    	}
    
    	void Remove(std::string p_sFilename)
    	{
    
    	}
    
    	std::string GetExtension(std::string p_sFilename)
    	{
    		return p_sFilename.substr(p_sFilename.rfind('.') + 1); //+1 to not include the dot...
    	}
    
    	MS3DModelManager* MS3DModels()
    	{
    		if(!m_MS3DModelManager)
    			m_MS3DModelManager = new MS3DModelManager();
    
    		return m_MS3DModelManager;
    	}
    
    	AIMotionManager* AIMotions()
    	{
    		if(!m_AIMotionManager)
    			m_AIMotionManager = new AIMotionManager();
    
    		return m_AIMotionManager;
    	}
    
    private:
    	MS3DModelManager* m_MS3DModelManager;
    	AIMotionManager* m_AIMotionManager;
    };
    
    class MS3DModelManager
    {
    	MS3DModel* Create(std::string p_sFilename)
    	{
    		MS3DModelRenderData* ModelData = new MS3DModelRenderData;
    		ModelData->Model = new MS3DModel;
    
    		m_Models.push_back(ModelData);
    
    		if(m_Resources.CheckExists(p_sFilename) == false)
    		{
    			std::cout << "Resource was not on the list!" << std::endl;
    			
    			m_Resources.Add(p_sFilename, ModelData->Model);  
    		}
    
    		return ModelData->Model;
    	}
    
    	void Destroy(std::string p_sFilename)
    	{
    
    	}
    
    private:
    	struct MS3DModelRenderData
    	{
    		float Position[3]; // will be changed to vectors eventually
    		float Orientation[3];
    		MS3DModel *Model;
    	};
    
    	std::vector< MS3DModelRenderData* > m_Models; // holds pointers to all MS3D Models
    	ResourceContainer< MS3DModel > m_Resources;
    };
    
    int main()
    {
    	Manager::Instance()->Resources()->Add("AttackShip.ms3d");
    	Manager::Instance()->Resources()->Add("AttackShip.ms3d");
    }
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

  2. #17
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    968
    I believe I'm about to take a new approach in this system...

    I'm going to remove the renderlist (the list of MS3D Models) from the manager, and "outsource" that to my rendering engine...

    Since various engines will be calling the resource creation and pulling functions - we already know what kind of manager we need to use, no more type switching.

    There will be a handle, and a Manager, both templates.

    The handle is essentially a proxy object, I.E -> an object that holds a raw pointer with an overloaded -> operator, so we don't throw raw pointers around, prevents possible memory leaks..

    The manager will manage a list of loaded resources, call a copy constructor on the handles if resources already exist...

    This way the resource manager can be encapsulated, and not intertwined with the creation of a render list... This will take a few days to code.
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  3. #18
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034
    I'm not sure exactly where you're going with this, but you are aware that the handle would be hard to .. handle if it was a template eh?

    I like the idea of using a different class to handle the resources, and not intertwine them into the render list, but then again that might be a little slow.

    About the dangling pointer problem, that would be solved by using IDs or a little further into your idea, smart pointers (http://www.gamedev.net/reference/art...ticle1954.asp). I'm personally am going with IDs, and a templated smart pointer (which doesn't need to be handled generically, and its generally just a DWORD) which notifies the manager when an ID is no longer being referenced. That's not really necessary, and could have been handled either way.

    I didnt even notice this was a render list, so I could be wrong about your intentions.
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

  4. #19
    Absent Minded Programmer
    Join Date
    May 2005
    Posts
    968
    Here is the gist of things


    The templated Handle
    Code:
    template< typename T_ >
    class Handle
    {
      template< typename T_ > friend class Manager;
      public:
        T_* operator-> (void) { return (mRawResource); }
    
      private:
        Handle (T_ *raw) : mRawResource(raw) { }
    
        T_* mRawResource;
    };
    The handle declares Manager a friend so that Manager can construct a handle using a raw pointer to a resource, but nobody else can. There are ways to circumvent this if you subscribe to the crowd of "friend is evil," but this is the most straightforward.

    The resource manager itself simply keeps a list of resources and their names. It is responsible for allocating these resources (it does so in LoadResource(), which I haven't written here) and freeing them as needed (either when the manger itself is destroyed, or when the manager decides the resource hasn't been accessed recently enough, or the client calls a method on the manager to forcibly release a resource). The manager might look like:

    Code:
    template< typename T_ >
    class Manager
    {
      public:
        Handle< T_ > RequestResource(const std::string &name)
        {
        std::map< std::string,T_* >::iterator  it = mResources.find(name);
          
          if(it == mResources.end())
            return LoadResource(name);
          return (Handle(it->second));
        }
    
      private:
        std::map< std::string,T_* >  mResources;
    };
    And there we are. Assuming you provide an appropriate copy constructor and operator= for Handle, the manager clients can pass resources around with value semantics, which are easy to deal with. The Handle, since its not a "smart pointer" in any way, doesn't have to deal with deletion of its pointer (although it could, see below). Resources can be easily looked up, loaded if they don't exist, and their lifetime is controlled by the manager. You don't need your extra layers of indirection with resource caches/managers/et cetera.

    Of course, this rather bare bones system has its own drawbacks: its still possible for a client to hold on to a handle after the resource manager is destroyed (thus destroying the resources and making the handles pointer dangle), for example. This could be solved by reference-counting the resources in some way, and have the Handle appropriately increment or decrement the reference count (in this case your approaching a situation where it may be better to have the manager hand out smart pointers, instead of hand-rolled Handle classes). You could have the Handle call a method on the manager to fetch the raw pointer in operator-> instead of storing the pointer itself (Handle would then only store the key and would need some way of accessing the manager, possibly via a static pointer).
    Sometimes I forget what I am doing when I enter a room, actually, quite often.

  5. #20
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034
    Well, thats the gist of your new method (the one I didn't completely understand was your old method), which is almost exactly the same as the one I linked (minus templates and reference counting). Which I've read, but good write-up anyhow.

    I would go with implimenting your last sentence, but do both. (In LoadResource store it in Manager, then in the Handle constructor retrieve the reference from Manager and store it.) As well as impliment reference counting because I can't see checking all the resources to see when they were last used as being efficient, or even safe. If you went that way you could drop that friendship as well . I'm not a friend hater, and agree with the C++ FAQ LITE, but the overall advantage of your last sentence overrules a simple friendship.
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Templated Generic Resource Manager, WIP..
    By Shamino in forum C++ Programming
    Replies: 13
    Last Post: 02-19-2006, 06:29 PM