Subversion Repositories gelsvn

Rev

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

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