Subversion Repositories gelsvn

Rev

Rev 39 | Rev 129 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#ifndef __RESOURCE_MANAGER_H
#define __RESOURCE_MANAGER_H

#include <cassert>
#include <string>
#include <list>
#include <typeinfo>

#define CLEAN_SHUTDOWN 1

namespace Util
{

        typedef unsigned char FlagByte;
        const FlagByte REMOVE_WHEN_UNUSED = 0x01;
        const FlagByte STATIC_RESOURCE    = 0x02;

        /** This class template represents a resource record. There is a pointer to 
                        the actual resource (of type RES) and a usage counter which should never
                        be less than 0. */
        template<class RES>
        class ResourceRecord
        {
                /// Name of the resource (const)
    const std::string name;

                /// A pointer to the resource (the pointer not the pointee is const)
                RES * const res;
                
                /// Usage counter.
                int usage;

                /// Flags
                FlagByte flags;

        public:
        
                /// Construct a null record.
                ResourceRecord(): res(0), usage(0), flags(0) {}

                /// Construct a resource record with a name and a pointer.
                ResourceRecord(const std::string& _name, RES* _res, bool static_res=false): 
                        name(_name), 
                        res(_res), 
                        usage(0), 
                        flags(static_res ? STATIC_RESOURCE : 0) 
                {
                        assert(res != 0);
                }

                ~ResourceRecord() 
                {
#if CLEAN_SHUTDOWN
                        assert(usage==0);               
#endif
                }
        
                void erase_resource()
                {
                        assert(usage==0);
                        if(!(flags&STATIC_RESOURCE)) 
                                {
                                        delete res;
                                }
                }

                /// Increment the usage counter
                void increment_usage() 
                {
                        assert(usage>=0);
                        ++usage;
                }

                /// Decrement the usage counter. assert that counter is >0
                void decrement_usage() 
                {
                        assert(usage>0);
                        --usage;
                }

                /// Return the usage count (mostly for debugging)
                int get_usage() const { return usage; }

                /// Get the name of a resource
                const std::string& get_name() const {return name;}

                /// Get a pointer to the resource (const pointer)
                RES * const get_ptr() 
                {
                        return res;
                }

                /// Get a resource pointer (const pointer and pointee)
                const RES * const get_ptr() const 
                {
                        return res;
                }

                void remove_when_unused() 
                {
/*                      assert(!(flags&STATIC_RESOURCE)); */
/*                      if(!(flags&STATIC_RESOURCE)) */
                                flags = flags|REMOVE_WHEN_UNUSED;
                }

                FlagByte get_flags() const { return flags;}
        
        };


        template<class RES> class ResourceManager;


        /** The ResourcePtr class template is a template for a reference
                        counted resource pointer. It is a smart pointer that actually points
                        to a ResourceRecord and not to the actual resource. Since the record
                        contains a reference count, we can increase the reference count when
                        the pointer is copied and decrease it in the destructor. Only the
                        ResourceManager can create ResourcePtr's directly from a raw
                        ResourceRecord. */
        template<class RES>
        class ResourcePtr
        {
                typedef ResourceRecord<RES> RR;
                typedef std::list<RR> RRList;
                typedef typename RRList::iterator RRListIter;

                friend class ResourceManager<RES>;

        private:
                RRListIter rr;
                RES* res;
        
                /** Explicit constructor with value. The constructor can only be called
                                by a friend, i.e. the resource manager. */
                explicit ResourcePtr(const RRListIter& _rr): rr(_rr), res(0)
                {
                        if(rr != 0)
                                {
                                        rr->increment_usage();
                                        res = rr->get_ptr();
                                }
                }

                void decrement_usage();
        
        public:
        
                ResourcePtr(): rr(0), res(0) {}
        
                ResourcePtr(const ResourcePtr& r2): rr(r2.rr), res(0)
                {
                        if(rr != 0)
                                {
                                        rr->increment_usage();
                                        res = rr->get_ptr();
                                }
                }

                const ResourcePtr& operator=(const ResourcePtr& r2) 
                {
                        // Guard against self-assignment
                        if (r2.rr != this->rr)
                                {
                                        if(rr != 0)     decrement_usage();

                                        rr  = r2.rr;
                                        res = 0;

                                        if(rr != 0)
                                                {
                                                        res = rr->get_ptr();
                                                        rr->increment_usage();
                                                }
                                }
                        return *this;
                }

                ~ResourcePtr() {decrement_usage();}

                RES& operator*() const 
                {
                        assert(rr != 0);
                        assert(res != 0);
                        return *res;
                }

                RES* const operator->() const 
                {
                        assert(rr != 0);
                        assert(res !=0);
                        return res;
                }

                RES* const get_raw_ptr() const 
                {
                        assert(rr != 0);
                        assert(res != 0);
                        return res;
                }

                int usage() const
                {
                        if(rr != 0)
                                return rr->get_usage();
                        return -1;
                }

                bool is_valid() const {return rr != 0;}

                void relinquish_resource()
                {
                        ResourcePtr p;
                        *this = p;
                }

                /** Calling this function sets the REMOVE_WHEN_UNUSED flag. If this 
                                flag is set, the resource record is removed from the manager */
                void remove_when_unused() {     rr->remove_when_unused(); }
        };


        /** The ResourceManager is a singleton, and it uses Scott Meyers construct
                        on first use idiom, i.e. the static function get_instance() will
                        construct it when first called. There may be a problem with this
                        idion if resources depend on each other. However, that is only
                        when the program shuts down.  See modern C++ design by
                        Alexandrescu for more information.

                        I'll try to make everything in this class private and accessed
                        by a simple interface of friend functions.

        */

        template<class RES>
        class ResourceManager
        {
                typedef ResourceRecord<RES> RR;
                typedef std::list<RR> RRList;
                typedef typename RRList::iterator RRListIter;
                RRList resources;
        
                ResourceManager(): resources(0) {}
                ResourceManager(const ResourceManager&);
                ResourceManager& operator=(const ResourceManager&);

        public:


                ~ResourceManager()
                {
#if CLEAN_SHUTDOWN
                        RRListIter i = resources.begin(); 
                        while(i != resources.end())
                                {
                                        if(i->get_usage()==0)
                                                {
                                                        RRListIter tmp = i;
                                                        ++i;
                                                        erase_resource(tmp);
                                                }
                                        else
                                                {
                                                        std::cout << "Warning, ResourceManager:\n\n"
                                                                                                << (typeid(this).name())
                                                                                                << "\n\nis shutting down, and resource \n\n" 
                                                                                                << i->get_name() << "\n\nhas usage: " << i->get_usage()
                                                                                                << std::endl;
                                                        std::cout <<
                                                                "In other words, this resource is not unused at this\n"
                                                                "point. That is unfortunate because then it cannot\n"
                                                                "be deleted (since it might be used by some other\n"
                                                                "part of the program during shutdown). Please ensure\n"
                                                                "that all resources are unused at program\n"
                                                                "termination. If you use a global ResourcePtr, this is\n"
                                                                "done by calling relinquish_resource just before\n"
                                                                "exiting.\n"
                                                                                                << std::endl;
                                                        ++i;
                                                }
                                }
#endif
                }

                int get_no_resources() const
                {
                        return resources.size();
                }

                static ResourceManager& get_instance() 
                {
                        static ResourceManager instance;
                        return instance;
                }

                void erase_resource(RRListIter iter)
                {
                        iter->erase_resource();
                        resources.erase(iter);
                }

                /** Find a resource and return a ResourcePtr to it. Returns the 
                                0 ResourcePtr if no string found. */
                ResourcePtr<RES> get_resource_ptr(const std::string& str)
                {
                        for(RRListIter i = resources.begin(); i != resources.end(); ++i)
                                if((*i).get_name() == str) 
                                        return ResourcePtr<RES>(i);
                        return ResourcePtr<RES>(0);
                }

                /** Add a resource with name passed as first argument and a pointer
                                passed as 2nd argument. A ResourcePtr to the just inserted
                                element is returned. */
                ResourcePtr<RES> register_resource(const std::string& str, 
                                                                                                                                                         RES* res, bool static_resource)
                {
                        for(RRListIter i = resources.begin(); i != resources.end(); ++i)
                                if(i->get_name() == str)
                                        return (ResourcePtr<RES>(0));

                        resources.push_front(RR(str, res, static_resource));
                        ResourcePtr<RES> ptr = ResourcePtr<RES>(resources.begin());
                        return ptr;
                }

/*              friend class ResourcePtr<RES>;
                friend int get_no_resources<RES>();
                friend ResourcePtr<RES> get_resource_ptr<RES>(const std::string& str); 
                friend ResourcePtr<RES> register_static_resource<RES>(const std::string&,
                                                                                                                                                                                                                                        RES*); 
                friend ResourcePtr<RES> register_dynamic_resource<RES>(const std::string&,
                                                                                                                                                                                                                                         RES*);*/ 
        };

        template<class RES>
        inline void ResourcePtr<RES>::decrement_usage()
        {
                assert( (rr == 0) || (res != 0));
                if(rr != 0)
                        {
                                rr->decrement_usage();
                                if(rr->get_usage() == 0 && (rr->get_flags()&REMOVE_WHEN_UNUSED))
                                        {
                                                ResourceManager<RES>& man = ResourceManager<RES>::get_instance();
                                                man.erase_resource(rr);
                                        }
                        }
        }

        template<class RES>
        inline int get_no_resources()
        {
                ResourceManager<RES>& man = ResourceManager<RES>::get_instance();
                return man.get_no_resources();
        }

        template<class RES>
        inline ResourcePtr<RES> get_resource_ptr(const std::string& str)
        {
                ResourceManager<RES>& man = ResourceManager<RES>::get_instance();
                return man.get_resource_ptr(str);
        }

        template<class RES>
        inline ResourcePtr<RES> register_dynamic_resource(const std::string& str,RES* res)
        {
                ResourceManager<RES>& man = ResourceManager<RES>::get_instance();
                ResourcePtr<RES> ptr = man.register_resource(str, res, false);
                return ptr;
        }

        template<class RES>
        inline ResourcePtr<RES> register_static_resource(const std::string& str,RES* res)
        {
                ResourceManager<RES>& man = ResourceManager<RES>::get_instance();
                ResourcePtr<RES> ptr = man.register_resource(str, res, true);
                return ptr;
        }

}


#endif