Subversion Repositories gelsvn

Rev

Rev 56 | Go to most recent revision | Details | Last modification | View Log | RSS feed

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