Subversion Repositories gelsvn

Rev

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

Rev Author Line No. Line
39 bj 1
#ifndef __RESOURCE_MANAGER_H
2
#define __RESOURCE_MANAGER_H
3
 
56 jab 4
#include <cassert>
39 bj 5
#include <string>
6
#include <list>
7
#include <typeinfo>
8
 
9
#define CLEAN_SHUTDOWN 1
10
 
56 jab 11
namespace Util
39 bj 12
{
13
 
14
	typedef unsigned char FlagByte;
15
	const FlagByte REMOVE_WHEN_UNUSED = 0x01;
16
	const FlagByte STATIC_RESOURCE    = 0x02;
17
 
89 jab 18
	/** \brief This class template represents a resource record. 
19
 
20
	    There is a pointer to 
39 bj 21
			the actual resource (of type RES) and a usage counter which should never
22
			be less than 0. */
23
	template<class RES>
24
	class ResourceRecord
25
	{
26
		/// Name of the resource (const)
27
    const std::string name;
28
 
29
		/// A pointer to the resource (the pointer not the pointee is const)
30
		RES * const res;
31
 
32
		/// Usage counter.
33
		int usage;
34
 
35
		/// Flags
36
		FlagByte flags;
37
 
38
	public:
39
 
40
		/// Construct a null record.
41
		ResourceRecord(): res(0), usage(0), flags(0) {}
42
 
43
		/// Construct a resource record with a name and a pointer.
44
		ResourceRecord(const std::string& _name, RES* _res, bool static_res=false): 
45
			name(_name), 
46
			res(_res), 
47
			usage(0), 
48
			flags(static_res ? STATIC_RESOURCE : 0) 
49
		{
50
			assert(res != 0);
51
		}
52
 
53
		~ResourceRecord() 
54
		{
55
#if CLEAN_SHUTDOWN
56
			assert(usage==0);		
57
#endif
58
		}
59
 
60
		void erase_resource()
61
		{
62
			assert(usage==0);
63
			if(!(flags&STATIC_RESOURCE)) 
64
				{
65
					delete res;
66
				}
67
		}
68
 
69
		/// Increment the usage counter
70
		void increment_usage() 
71
		{
72
			assert(usage>=0);
73
			++usage;
74
		}
75
 
56 jab 76
		/// Decrement the usage counter. assert that counter is >0
39 bj 77
		void decrement_usage() 
78
		{
79
			assert(usage>0);
80
			--usage;
81
		}
82
 
83
		/// Return the usage count (mostly for debugging)
84
		int get_usage() const { return usage; }
85
 
86
		/// Get the name of a resource
87
		const std::string& get_name() const {return name;}
88
 
89
		/// Get a pointer to the resource (const pointer)
90
		RES * const get_ptr() 
91
		{
92
			return res;
93
		}
94
 
95
		/// Get a resource pointer (const pointer and pointee)
96
		const RES * const get_ptr() const 
97
		{
98
			return res;
99
		}
100
 
101
		void remove_when_unused() 
102
		{
103
/* 			assert(!(flags&STATIC_RESOURCE)); */
104
/* 			if(!(flags&STATIC_RESOURCE)) */
105
				flags = flags|REMOVE_WHEN_UNUSED;
106
		}
107
 
108
		FlagByte get_flags() const { return flags;}
109
 
110
	};
111
 
112
 
113
	template<class RES> class ResourceManager;
114
 
115
 
89 jab 116
	/** \brief Template for a pointer to a reference counted resource.
117
 
118
      The ResourcePtr class template is a template for a reference
39 bj 119
			counted resource pointer. It is a smart pointer that actually points
120
			to a ResourceRecord and not to the actual resource. Since the record
121
			contains a reference count, we can increase the reference count when
122
			the pointer is copied and decrease it in the destructor. Only the
123
			ResourceManager can create ResourcePtr's directly from a raw
124
			ResourceRecord. */
125
	template<class RES>
126
	class ResourcePtr
127
	{
128
		typedef ResourceRecord<RES> RR;
129
		typedef std::list<RR> RRList;
130
		typedef typename RRList::iterator RRListIter;
131
 
132
		friend class ResourceManager<RES>;
133
 
134
	private:
135
		RRListIter rr;
136
		RES* res;
137
 
138
		/** Explicit constructor with value. The constructor can only be called
139
				by a friend, i.e. the resource manager. */
140
		explicit ResourcePtr(const RRListIter& _rr): rr(_rr), res(0)
141
		{
142
			if(rr != 0)
143
				{
144
					rr->increment_usage();
145
					res = rr->get_ptr();
146
				}
147
		}
148
 
149
		void decrement_usage();
150
 
151
	public:
152
 
153
		ResourcePtr(): rr(0), res(0) {}
154
 
155
		ResourcePtr(const ResourcePtr& r2): rr(r2.rr), res(0)
156
		{
157
			if(rr != 0)
158
				{
159
					rr->increment_usage();
160
					res = rr->get_ptr();
161
				}
162
		}
163
 
164
		const ResourcePtr& operator=(const ResourcePtr& r2) 
165
		{
166
			// Guard against self-assignment
167
			if (r2.rr != this->rr)
168
				{
169
					if(rr != 0)	decrement_usage();
170
 
171
					rr  = r2.rr;
172
					res = 0;
173
 
174
					if(rr != 0)
175
						{
176
							res = rr->get_ptr();
177
							rr->increment_usage();
178
						}
179
				}
180
			return *this;
181
		}
182
 
183
		~ResourcePtr() {decrement_usage();}
184
 
185
		RES& operator*() const 
186
		{
187
			assert(rr != 0);
188
			assert(res != 0);
189
			return *res;
190
		}
191
 
192
		RES* const operator->() const 
193
		{
194
			assert(rr != 0);
195
			assert(res !=0);
196
			return res;
197
		}
198
 
199
		RES* const get_raw_ptr() const 
200
		{
201
			assert(rr != 0);
202
			assert(res != 0);
203
			return res;
204
		}
205
 
206
		int usage() const
207
		{
208
			if(rr != 0)
209
				return rr->get_usage();
210
			return -1;
211
		}
212
 
213
		bool is_valid() const {return rr != 0;}
214
 
215
		void relinquish_resource()
216
		{
217
			ResourcePtr p;
218
			*this = p;
219
		}
220
 
221
		/** Calling this function sets the REMOVE_WHEN_UNUSED flag. If this 
222
				flag is set, the resource record is removed from the manager */
223
		void remove_when_unused() {	rr->remove_when_unused(); }
224
	};
225
 
226
 
89 jab 227
	/** \brief Resource manager class.
228
 
229
      The ResourceManager is a singleton, and it uses Scott Meyers construct
39 bj 230
			on first use idiom, i.e. the static function get_instance() will
231
			construct it when first called. There may be a problem with this
232
			idion if resources depend on each other. However, that is only
233
			when the program shuts down.  See modern C++ design by
234
			Alexandrescu for more information.
235
 
236
			I'll try to make everything in this class private and accessed
237
			by a simple interface of friend functions.
238
 
239
	*/
240
 
241
	template<class RES>
242
	class ResourceManager
243
	{
244
		typedef ResourceRecord<RES> RR;
245
		typedef std::list<RR> RRList;
246
		typedef typename RRList::iterator RRListIter;
247
		RRList resources;
248
 
249
		ResourceManager(): resources(0) {}
250
		ResourceManager(const ResourceManager&);
251
		ResourceManager& operator=(const ResourceManager&);
252
 
253
	public:
254
 
255
 
256
		~ResourceManager()
257
		{
258
#if CLEAN_SHUTDOWN
259
			RRListIter i = resources.begin(); 
260
			while(i != resources.end())
261
				{
262
					if(i->get_usage()==0)
263
						{
264
							RRListIter tmp = i;
265
							++i;
266
							erase_resource(tmp);
267
						}
268
					else
269
						{
270
							std::cout << "Warning, ResourceManager:\n\n"
271
												<< (typeid(this).name())
272
												<< "\n\nis shutting down, and resource \n\n" 
273
												<< i->get_name() << "\n\nhas usage: " << i->get_usage()
274
												<< std::endl;
275
							std::cout <<
276
 								"In other words, this resource is not unused at this\n"
277
								"point. That is unfortunate because then it cannot\n"
278
								"be deleted (since it might be used by some other\n"
279
								"part of the program during shutdown). Please ensure\n"
280
								"that all resources are unused at program\n"
281
								"termination. If you use a global ResourcePtr, this is\n"
282
								"done by calling relinquish_resource just before\n"
283
								"exiting.\n"
284
												<< std::endl;
285
							++i;
286
						}
287
				}
288
#endif
289
		}
290
 
291
		int get_no_resources() const
292
		{
293
			return resources.size();
294
		}
295
 
296
		static ResourceManager& get_instance() 
297
		{
298
			static ResourceManager instance;
299
			return instance;
300
		}
301
 
302
		void erase_resource(RRListIter iter)
303
		{
304
			iter->erase_resource();
305
			resources.erase(iter);
306
		}
307
 
308
		/** Find a resource and return a ResourcePtr to it. Returns the 
309
 
310
		ResourcePtr<RES> get_resource_ptr(const std::string& str)
311
		{
312
			for(RRListIter i = resources.begin(); i != resources.end(); ++i)
313
				if((*i).get_name() == str) 
314
					return ResourcePtr<RES>(i);
315
			return ResourcePtr<RES>(0);
316
		}
317
 
318
		/** Add a resource with name passed as first argument and a pointer
319
				passed as 2nd argument. A ResourcePtr to the just inserted
320
				element is returned. */
321
		ResourcePtr<RES> register_resource(const std::string& str, 
322
																			 RES* res, bool static_resource)
323
		{
324
			for(RRListIter i = resources.begin(); i != resources.end(); ++i)
325
				if(i->get_name() == str)
326
					return (ResourcePtr<RES>(0));
327
 
328
			resources.push_front(RR(str, res, static_resource));
329
			ResourcePtr<RES> ptr = ResourcePtr<RES>(resources.begin());
330
			return ptr;
331
		}
332
 
333
/*		friend class ResourcePtr<RES>;
334
		friend int get_no_resources<RES>();
335
		friend ResourcePtr<RES> get_resource_ptr<RES>(const std::string& str); 
336
		friend ResourcePtr<RES> register_static_resource<RES>(const std::string&,
337
																													RES*); 
338
		friend ResourcePtr<RES> register_dynamic_resource<RES>(const std::string&,
339
																													 RES*);*/ 
340
	};
341
 
342
	template<class RES>
343
	inline void ResourcePtr<RES>::decrement_usage()
344
	{
345
		assert( (rr == 0) || (res != 0));
346
		if(rr != 0)
347
			{
348
				rr->decrement_usage();
349
				if(rr->get_usage() == 0 && (rr->get_flags()&REMOVE_WHEN_UNUSED))
350
					{
351
						ResourceManager<RES>& man = ResourceManager<RES>::get_instance();
352
						man.erase_resource(rr);
353
					}
354
			}
355
	}
356
 
357
	template<class RES>
358
	inline int get_no_resources()
359
	{
360
		ResourceManager<RES>& man = ResourceManager<RES>::get_instance();
361
		return man.get_no_resources();
362
	}
363
 
364
	template<class RES>
365
	inline ResourcePtr<RES> get_resource_ptr(const std::string& str)
366
	{
367
		ResourceManager<RES>& man = ResourceManager<RES>::get_instance();
368
		return man.get_resource_ptr(str);
369
	}
370
 
371
	template<class RES>
372
	inline ResourcePtr<RES> register_dynamic_resource(const std::string& str,RES* res)
373
	{
374
		ResourceManager<RES>& man = ResourceManager<RES>::get_instance();
375
		ResourcePtr<RES> ptr = man.register_resource(str, res, false);
376
		return ptr;
377
	}
378
 
379
	template<class RES>
380
	inline ResourcePtr<RES> register_static_resource(const std::string& str,RES* res)
381
	{
382
		ResourceManager<RES>& man = ResourceManager<RES>::get_instance();
383
		ResourcePtr<RES> ptr = man.register_resource(str, res, true);
384
		return ptr;
385
	}
386
 
387
}
388
 
389
 
390
#endif