Subversion Repositories gelsvn

Rev

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