Actual source code: ALE_mem.hh
1: #ifndef included_ALE_mem_hh
2: #define included_ALE_mem_hh
3: // This should be included indirectly -- only by including ALE.hh
5: #include <deque>
6: #include <iostream>
7: #include <map>
8: #include <memory>
9: #include <typeinfo>
10: #include <petsc.h>
11: #include <ALE_log.hh>
13: #ifdef ALE_HAVE_CXX_ABI
14: #include <cxxabi.h>
15: #endif
17: namespace ALE {
18: class MemoryLogger {
19: public:
20: struct Log {
21: int num;
22: int total;
23: std::map<std::string, int> items;
25: Log(): num(0), total(0) {};
26: };
27: typedef std::map<std::string, std::pair<Log, Log> > stageLog;
28: typedef std::deque<std::string> names;
29: protected:
30: int _debug;
31: MPI_Comm comm;
32: int rank;
33: names stageNames;
34: stageLog stages;
35: protected:
36: MemoryLogger(): _debug(0), comm(PETSC_COMM_WORLD) {
37: MPI_Comm_rank(comm, &rank);
38: stageNames.push_front("default");
39: };
40: public:
41: ~MemoryLogger() {};
42: static MemoryLogger& singleton() {
43: static MemoryLogger singleton;
45: return singleton;
46: };
47: int debug() {return _debug;};
48: void setDebug(int debug) {_debug = debug;};
49: public:
50: void stagePush(const std::string& name) {stageNames.push_front(name);};
51: void stagePop() {stageNames.pop_front();};
52: void logAllocation(const std::string& className, int bytes) {logAllocation(stageNames.front(), className, bytes);};
53: void logAllocation(const std::string& stage, const std::string& className, int bytes) {
54: if (_debug) {std::cout << "["<<rank<<"]Allocating " << bytes << " bytes for class " << className << std::endl;}
55: stages[stage].first.num++;
56: stages[stage].first.total += bytes;
57: stages[stage].first.items[className] += bytes;
58: };
59: void logDeallocation(const std::string& className, int bytes) {logDeallocation(stageNames.front(), className, bytes);};
60: void logDeallocation(const std::string& stage, const std::string& className, int bytes) {
61: if (_debug) {std::cout << "["<<rank<<"]Deallocating " << bytes << " bytes for class " << className << std::endl;}
62: stages[stage].second.num++;
63: stages[stage].second.total += bytes;
64: stages[stage].second.items[className] += bytes;
65: };
66: public:
67: int getNumAllocations() {return getNumAllocations(stageNames.front());};
68: int getNumAllocations(const std::string& stage) {return stages[stage].first.num;};
69: int getNumDeallocations() {return getNumDeallocations(stageNames.front());};
70: int getNumDeallocations(const std::string& stage) {return stages[stage].second.num;};
71: int getAllocationTotal() {return getAllocationTotal(stageNames.front());};
72: int getAllocationTotal(const std::string& stage) {return stages[stage].first.total;};
73: int getDeallocationTotal() {return getDeallocationTotal(stageNames.front());};
74: int getDeallocationTotal(const std::string& stage) {return stages[stage].second.total;};
75: public:
76: template<typename T>
77: static const char *getClassName() {
78: const std::type_info& id = typeid(T);
80: #ifdef ALE_HAVE_CXX_ABI
81: // If the C++ ABI API is available, we can use it to demangle the class name provided by type_info.
82: // Here we assume the industry standard C++ ABI as described in http://www.codesourcery.com/cxx-abi/abi.html.
83: char *id_name;
84: int status;
85: char *id_name_demangled = abi::__cxa_demangle(id.name(), NULL, NULL, &status);
87: if (status != 0) {
88: id_name = new char[strlen(id.name())+1];
89: strcpy(id_name, id.name());
90: } else {
91: id_name = id_name_demangled;
92: }
93: #else
94: const char *id_name;
96: id_name = id.name();
97: #endif
98: return id_name;
99: };
100: static void restoreClassName(const char *className) {
101: #ifdef ALE_HAVE_CXX_ABI
102: // Free the name malloc'ed by __cxa_demangle
103: free(const_cast<char *>(className));
104: #endif
105: };
106: };
108: template<class T>
109: class malloc_allocator
110: {
111: public:
112: typedef T value_type;
113: typedef value_type* pointer;
114: typedef const value_type* const_pointer;
115: typedef value_type& reference;
116: typedef const value_type& const_reference;
117: typedef std::size_t size_type;
118: typedef std::ptrdiff_t difference_type;
119: public:
120: template <class U>
121: struct rebind {typedef malloc_allocator<U> other;};
122: protected:
123: const char *className;
124: public:
125: int sz;
126: public:
127: #ifdef ALE_MEM_LOGGING
128: malloc_allocator() {className = ALE::MemoryLogger::getClassName<T>();sz = sizeof(value_type);}
129: malloc_allocator(const malloc_allocator&) {className = ALE::MemoryLogger::getClassName<T>();sz = sizeof(value_type);}
130: template <class U>
131: malloc_allocator(const malloc_allocator<U>&) {className = ALE::MemoryLogger::getClassName<T>();sz = sizeof(value_type);}
132: ~malloc_allocator() {ALE::MemoryLogger::restoreClassName(className);}
133: #else
134: malloc_allocator() {sz = sizeof(value_type);}
135: malloc_allocator(const malloc_allocator&) {sz = sizeof(value_type);}
136: template <class U>
137: malloc_allocator(const malloc_allocator<U>&) {sz = sizeof(value_type);}
138: ~malloc_allocator() {}
139: #endif
140: public:
141: pointer address(reference x) const {return &x;}
142: const_pointer address(const_reference x) const {return x;}
144: pointer allocate(size_type n, const_pointer = 0) {
145: #ifdef ALE_MEM_LOGGING
146: ALE::MemoryLogger::singleton().logAllocation(className, n * sizeof(T));
147: #endif
148: void *p = std::malloc(n * sizeof(T));
149: if (!p) throw std::bad_alloc();
150: return static_cast<pointer>(p);
151: }
153: void deallocate(pointer p, size_type n) {
154: #ifdef ALE_MEM_LOGGING
155: ALE::MemoryLogger::singleton().logDeallocation(className, n * sizeof(T));
156: #endif
157: std::free(p);
158: }
160: size_type max_size() const {return static_cast<size_type>(-1) / sizeof(T);}
162: void construct(pointer p, const value_type& x) {new(p) value_type(x);}
164: void destroy(pointer p) {p->~value_type();}
165: public:
166: pointer create(const value_type& x = value_type()) {
167: pointer p = (pointer) allocate(1);
168: construct(p, x);
169: return p;
170: };
172: void del(pointer p) {
173: destroy(p);
174: deallocate(p, 1);
175: };
177: // This is just to be compatible with Dmitry's weird crap for now
178: void del(pointer p, size_type size) {
179: if (size != sizeof(value_type)) throw std::exception();
180: destroy(p);
181: deallocate(p, 1);
182: };
183: private:
184: void operator=(const malloc_allocator&);
185: };
187: template<> class malloc_allocator<void>
188: {
189: typedef void value_type;
190: typedef void* pointer;
191: typedef const void* const_pointer;
193: template <class U>
194: struct rebind {typedef malloc_allocator<U> other;};
195: };
197: template <class T>
198: inline bool operator==(const malloc_allocator<T>&, const malloc_allocator<T>&) {
199: return true;
200: };
202: template <class T>
203: inline bool operator!=(const malloc_allocator<T>&, const malloc_allocator<T>&) {
204: return false;
205: };
207: template <class T>
208: static const char *getClassName() {
209: const std::type_info& id = typeid(T);
210: const char *id_name;
212: #ifdef ALE_HAVE_CXX_ABI
213: // If the C++ ABI API is available, we can use it to demangle the class name provided by type_info.
214: // Here we assume the industry standard C++ ABI as described in http://www.codesourcery.com/cxx-abi/abi.html.
215: int status;
216: char *id_name_demangled = abi::__cxa_demangle(id.name(), NULL, NULL, &status);
218: if (status != 0) {
219: id_name = id.name();
220: } else {
221: id_name = id_name_demangled;
222: }
223: #else
224: id_name = id.name();
225: #endif
226: return id_name;
227: };
228: template <class T>
229: static void restoreClassName(const char *id_name) {
230: #ifdef ALE_HAVE_CXX_ABI
231: // Free the name malloc'ed by __cxa_demangle
232: free((char *) id_name);
233: #endif
234: };
236: // This UNIVERSAL allocator class is static and provides allocation/deallocation services to all allocators defined below.
237: class universal_allocator {
238: public:
239: typedef std::size_t size_type;
240: static char* allocate(const size_type& sz);
241: static void deallocate(char *p, const size_type& sz);
242: static size_type max_size();
243: };
245: // This allocator implements create and del methods, that act roughly as new and delete in that they invoke a constructor/destructor
246: // in addition to memory allocation/deallocation.
247: // An additional (and potentially dangerous) feature allows an object of any type to be deleted so long as its size has been provided.
248: template <class T>
249: class polymorphic_allocator {
250: public:
251: typedef typename std::allocator<T> Alloc;
252: // A specific allocator -- alloc -- of type Alloc is used to define the correct types and implement methods
253: // that do not allocate/deallocate memory themselves -- the universal _alloc is used for that (and only that).
254: // The relative size sz is used to calculate the amount of memory to request from _alloc to satisfy a request to alloc.
255: typedef typename Alloc::size_type size_type;
256: typedef typename Alloc::difference_type difference_type;
257: typedef typename Alloc::pointer pointer;
258: typedef typename Alloc::const_pointer const_pointer;
259: typedef typename Alloc::reference reference;
260: typedef typename Alloc::const_reference const_reference;
261: typedef typename Alloc::value_type value_type;
263: static Alloc alloc; // The underlying specific allocator
264: static typename Alloc::size_type sz; // The size of T universal units of char
266: polymorphic_allocator() {};
267: polymorphic_allocator(const polymorphic_allocator& a) {};
268: template <class TT>
269: polymorphic_allocator(const polymorphic_allocator<TT>& aa){};
270: ~polymorphic_allocator() {};
272: // Reproducing the standard allocator interface
273: pointer address(reference _x) const { return alloc.address(_x); };
274: const_pointer address(const_reference _x) const { return alloc.address(_x); };
275: T* allocate(size_type _n) { return (T*)universal_allocator::allocate(_n*sz); };
276: void deallocate(pointer _p, size_type _n) { universal_allocator::deallocate((char*)_p, _n*sz); };
277: void construct(pointer _p, const T& _val) { alloc.construct(_p, _val); };
278: void destroy(pointer _p) { alloc.destroy(_p); };
279: size_type max_size() const { return (size_type)floor(universal_allocator::max_size()/sz); };
280: // conversion typedef
281: template <class TT>
282: struct rebind { typedef polymorphic_allocator<TT> other;};
283:
284: T* create(const T& _val = T());
285: void del(T* _p);
286: template<class TT> void del(TT* _p, size_type _sz);
287: };
289: template <class T>
290: typename polymorphic_allocator<T>::Alloc polymorphic_allocator<T>::alloc;
292: //IMPORTANT: allocator 'sz' calculation takes place here
293: template <class T>
294: typename polymorphic_allocator<T>::size_type polymorphic_allocator<T>::sz =
295: (typename polymorphic_allocator<T>::size_type)(ceil(sizeof(T)/sizeof(char)));
297: template <class T>
298: T* polymorphic_allocator<T>::create(const T& _val) {
299: // First, allocate space for a single object
300: T* _p = (T*)universal_allocator::allocate(sz);
301: // Construct an object in the provided space using the provided initial value
302: this->alloc.construct(_p, _val);
303: return _p;
304: }
306: template <class T>
307: void polymorphic_allocator<T>::del(T* _p) {
308: _p->~T();
309: universal_allocator::deallocate((char*)_p, polymorphic_allocator<T>::sz);
310: }
312: template <class T> template <class TT>
313: void polymorphic_allocator<T>::del(TT* _p, size_type _sz) {
314: _p->~TT();
315: universal_allocator::deallocate((char*)_p, _sz);
316: }
319: // An allocator all of whose events (allocation, deallocation, new, delete) are logged using ALE_log facilities.
320: // O is true if this is an Obj allocator (that's the intended use, anyhow).
321: template <class T, bool O = false>
322: class logged_allocator : public polymorphic_allocator<T> {
323: private:
324: static bool _log_initialized;
325: static LogCookie _cookie;
326: static int _allocate_event;
327: static int _deallocate_event;
328: static int _construct_event;
329: static int _destroy_event;
330: static int _create_event;
331: static int _del_event;
332: //
333: static void __log_initialize();
334: static LogEvent __log_event_register(const char *class_name, const char *event_name);
335: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
336: // FIX: should PETSc memory logging machinery be wrapped by ALE_log like the rest of the logging stuff?
337: PetscObject _petscObj; // this object is used to log memory in PETSc
338: #endif
339: void __alloc_initialize();
340: void __alloc_finalize();
341: public:
342: // Present the correct allocator interface
343: typedef typename polymorphic_allocator<T>::size_type size_type;
344: typedef typename polymorphic_allocator<T>::difference_type difference_type;
345: typedef typename polymorphic_allocator<T>::pointer pointer;
346: typedef typename polymorphic_allocator<T>::const_pointer const_pointer;
347: typedef typename polymorphic_allocator<T>::reference reference;
348: typedef typename polymorphic_allocator<T>::const_reference const_reference;
349: typedef typename polymorphic_allocator<T>::value_type value_type;
350: //
351: logged_allocator() : polymorphic_allocator<T>() {__log_initialize(); __alloc_initialize();};
352: logged_allocator(const logged_allocator& a) : polymorphic_allocator<T>(a) {__log_initialize(); __alloc_initialize();};
353: template <class TT>
354: logged_allocator(const logged_allocator<TT>& aa) : polymorphic_allocator<T>(aa){__log_initialize(); __alloc_initialize();};
355: ~logged_allocator() {__alloc_finalize();};
356: // conversion typedef
357: template <class TT>
358: struct rebind { typedef logged_allocator<TT> other;};
360: T* allocate(size_type _n);
361: void deallocate(T* _p, size_type _n);
362: void construct(T* _p, const T& _val);
363: void destroy(T* _p);
365: T* create(const T& _val = T());
366: void del(T* _p);
367: template <class TT> void del(TT* _p, size_type _sz);
368: };
370: template <class T, bool O>
371: bool logged_allocator<T, O>::_log_initialized(false);
372: template <class T, bool O>
373: LogCookie logged_allocator<T,O>::_cookie(0);
374: template <class T, bool O>
375: int logged_allocator<T, O>::_allocate_event(0);
376: template <class T, bool O>
377: int logged_allocator<T, O>::_deallocate_event(0);
378: template <class T, bool O>
379: int logged_allocator<T, O>::_construct_event(0);
380: template <class T, bool O>
381: int logged_allocator<T, O>::_destroy_event(0);
382: template <class T, bool O>
383: int logged_allocator<T, O>::_create_event(0);
384: template <class T, bool O>
385: int logged_allocator<T, O>::_del_event(0);
386:
387: template <class T, bool O>
388: void logged_allocator<T, O>::__log_initialize() {
389: if(!logged_allocator::_log_initialized) {
390: // First of all we make sure PETSc is initialized
391: PetscTruth flag;
392: PetscErrorCode PetscInitialized(&flag); CHKERROR(ierr, "Error in PetscInitialized");
393: if(!flag) {
394: // I guess it would be nice to initialize PETSc here, but we'd need argv/argc here
395: throw ALE::Exception("PETSc not initialized");
396: }
397: // Get a new cookie based on the class name
398: const char *id_name = ALE::getClassName<T>();
399: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
400: // Use id_name to register a cookie and events.
401: logged_allocator::_cookie = LogCookieRegister(id_name);
402: // Register the basic allocator methods' invocations as events; use the mangled class name.
403: logged_allocator::_allocate_event = logged_allocator::__log_event_register(id_name, "allocate");
404: logged_allocator::_deallocate_event = logged_allocator::__log_event_register(id_name, "deallocate");
405: logged_allocator::_construct_event = logged_allocator::__log_event_register(id_name, "construct");
406: logged_allocator::_destroy_event = logged_allocator::__log_event_register(id_name, "destroy");
407: logged_allocator::_create_event = logged_allocator::__log_event_register(id_name, "create");
408: logged_allocator::_del_event = logged_allocator::__log_event_register(id_name, "del");
409: #endif
410: ALE::restoreClassName<T>(id_name);
411: logged_allocator::_log_initialized = true;
412: }// if(!!logged_allocator::_log_initialized)
413: }// logged_allocator<T,O>::__log_initialize()
415: template <class T, bool O>
416: void logged_allocator<T, O>::__alloc_initialize() {
417: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
418: const char *id_name = ALE::getClassName<T>();
419: // PetscErrorCode PetscObjectCreateGeneric(PETSC_COMM_WORLD, logged_allocator::_cookie, id_name, &this->_petscObj);
420: // CHKERROR(ierr, "Failed in PetscObjectCreate");
421: ALE::restoreClassName<T>(id_name);
422: #endif
423: }// logged_allocator<T,O>::__alloc_initialize
425: template <class T, bool O>
426: void logged_allocator<T, O>::__alloc_finalize() {
427: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
428: // if (this->_petscObj) {
429: // PetscErrorCode PetscObjectDestroy(this->_petscObj);
430: // CHKERROR(ierr, "Failed in PetscObjectDestroy");
431: // }
432: #endif
433: }// logged_allocator<T,O>::__alloc_finalize
435: template <class T, bool O>
436: LogEvent logged_allocator<T, O>::__log_event_register(const char *class_name, const char *event_name){
437: // This routine assumes a cookie has been obtained.
438: ostringstream txt;
439: if(O) {
440: txt << "Obj:";
441: }
442: #ifdef ALE_LOGGING_VERBOSE
443: txt << class_name;
444: #else
445: txt << "<allocator>";
446: #endif
447: txt << ":" << event_name;
448: return LogEventRegister(logged_allocator::_cookie, txt.str().c_str());
449: }
451: template <class T, bool O>
452: T* logged_allocator<T, O>::allocate(size_type _n) {
453: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
454: LogEventBegin(logged_allocator::_allocate_event);
455: #endif
456: T* _p = polymorphic_allocator<T>::allocate(_n);
457: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
458: // PetscErrorCode PetscLogObjectMemory(this->_petscObj, _n*polymorphic_allocator<T>::sz);
459: // CHKERROR(ierr, "Error in PetscLogObjectMemory");
460: LogEventEnd(logged_allocator::_allocate_event);
461: #endif
462: return _p;
463: }
464:
465: template <class T, bool O>
466: void logged_allocator<T, O>::deallocate(T* _p, size_type _n) {
467: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
468: LogEventBegin(logged_allocator::_deallocate_event);
469: #endif
470: polymorphic_allocator<T>::deallocate(_p, _n);
471: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
472: LogEventEnd(logged_allocator::_deallocate_event);
473: #endif
474: }
475:
476: template <class T, bool O>
477: void logged_allocator<T, O>::construct(T* _p, const T& _val) {
478: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
479: LogEventBegin(logged_allocator::_construct_event);
480: #endif
481: polymorphic_allocator<T>::construct(_p, _val);
482: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
483: LogEventEnd(logged_allocator::_construct_event);
484: #endif
485: }
486:
487: template <class T, bool O>
488: void logged_allocator<T, O>::destroy(T* _p) {
489: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
490: LogEventBegin(logged_allocator::_destroy_event);
491: #endif
492: polymorphic_allocator<T>::destroy(_p);
493: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
494: LogEventEnd(logged_allocator::_destroy_event);
495: #endif
496: }
497:
498: template <class T, bool O>
499: T* logged_allocator<T, O>::create(const T& _val) {
500: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
501: LogEventBegin(logged_allocator::_create_event);
502: #endif
503: T* _p = polymorphic_allocator<T>::create(_val);
504: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
505: // PetscErrorCode PetscLogObjectMemory(this->_petscObj, polymorphic_allocator<T>::sz);
506: // CHKERROR(ierr, "Error in PetscLogObjectMemory");
507: LogEventEnd(logged_allocator::_create_event);
508: #endif
509: return _p;
510: }
512: template <class T, bool O>
513: void logged_allocator<T, O>::del(T* _p) {
514: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
515: LogEventBegin(logged_allocator::_del_event);
516: #endif
517: polymorphic_allocator<T>::del(_p);
518: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
519: LogEventEnd(logged_allocator::_del_event);
520: #endif
521: }
523: template <class T, bool O> template <class TT>
524: void logged_allocator<T, O>::del(TT* _p, size_type _sz) {
525: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
526: LogEventBegin(logged_allocator::_del_event);
527: #endif
528: polymorphic_allocator<T>::del(_p, _sz);
529: #if defined ALE_USE_LOGGING && defined ALE_LOGGING_LOG_MEM
530: LogEventEnd(logged_allocator::_del_event);
531: #endif
532: }
534: #ifdef ALE_USE_LOGGING
535: #define ALE_ALLOCATOR ::ALE::logged_allocator
536: #else
537: #if 1
538: #define ALE_ALLOCATOR ::ALE::malloc_allocator
539: #else
540: #define ALE_ALLOCATOR ::ALE::polymorphic_allocator
541: #endif
542: #endif
544: //
545: // The following classes define smart pointer behavior.
546: // They rely on allocators for memory pooling and logging (if logging is on).
547: //
549: // This is an Obj<X>-specific exception that is thrown when incompatible object conversion is attempted.
550: class BadCast : public Exception {
551: public:
552: explicit BadCast(const string& msg) : Exception(msg) {};
553: explicit BadCast(const ostringstream& txt) : Exception(txt) {};
554: // It actually looks like passing txt as an argument to Exception(ostringstream) performs a copy of txt,
555: // which is disallowed due to the ostringstream constructor being private; must use a string constructor.
556: BadCast(const BadCast& e) : Exception(e) {};
557: };
559: // This is the main smart pointer class.
560: template<class X, typename A = malloc_allocator<X> >
561: class Obj {
562: public:
563: // Types
564: #if 1
565: typedef A Allocator;
566: typedef typename Allocator::template rebind<int>::other Allocator_int;
567: #else
568: #ifdef ALE_USE_LOGGING
569: typedef logged_allocator<X,true> Allocator;
570: typedef logged_allocator<int,true> Allocator_int;
571: #else
572: typedef polymorphic_allocator<X> Allocator;
573: typedef polymorphic_allocator<int> Allocator_int;
574: #endif
575: #endif
576: typedef typename Allocator::size_type size_type;
577: public:
578: // These are intended to be private or at least protected
579: // allocators
580: Allocator_int *int_allocator;
581: Allocator *allocator;
582: //
583: X* objPtr; // object pointer
584: int* refCnt; // reference count
585: size_type sz; // Size of underlying object (universal units) allocated with an allocator; indicates allocator use.
586: // Constructor; this can be made private, if we move operator Obj<Y> outside this class definition and make it a friend.
587: Obj(X *xx, int *refCnt, size_type sz);
588: public:
589: // Constructors & a destructor
590: Obj() : objPtr((X *)NULL), refCnt((int*)NULL), sz(0) {this->createAllocators();};
591: Obj(const X& x);
592: Obj(X *xx);
593: Obj(X *xx, size_type sz);
594: Obj(const Obj& obj);
595: virtual ~Obj();
597: // "Factory" methods
598: Obj& create(const X& x = X());
599: void destroy();
600: void createAllocators();
601: void destroyAllocators();
602: void handleAllocators(const bool);
604: // predicates & assertions
605: bool isNull() const {return (this->objPtr == NULL);};
606: void assertNull(bool flag) const { if(this->isNull() != flag){ throw(Exception("Null assertion failed"));}};
608: // comparison operators
609: bool operator==(const Obj& obj) { return (this->objPtr == obj.objPtr);};
610: bool operator!=(const Obj& obj) { return (this->objPtr != obj.objPtr);};
611:
612: // assignment/conversion operators
613: Obj& operator=(const Obj& obj);
614: template <class Y> operator Obj<Y> const();
615: template <class Y> Obj& operator=(const Obj<Y>& obj);
617: // dereference operators
618: X* operator->() const {return objPtr;};
619:
620: // "exposure" methods: expose the underlying object or object pointer
621: operator X*() {return objPtr;};
622: X& operator*() const {assertNull(false); return *objPtr;};
623: operator X() {assertNull(false); return *objPtr;};
624: template<class Y> Obj& copy(const Obj<Y>& obj); // this operator will copy the underlying objects: USE WITH CAUTION
625:
627: // depricated methods/operators
628: X* ptr() const {return objPtr;};
629: X* pointer() const {return objPtr;};
630: X obj() const {assertNull(false); return *objPtr;};
631: X object() const {assertNull(false); return *objPtr;};
633: void addRef() {if (refCnt) {(*refCnt)++;}}
634: };// class Obj<X>
636: // Constructors
637: // New reference
638: template <class X, typename A>
639: Obj<X,A>::Obj(const X& x) {
640: this->createAllocators();
641: this->refCnt = NULL;
642: this->create(x);
643: }
644:
645: // Stolen reference
646: template <class X, typename A>
647: Obj<X,A>::Obj(X *xx){// such an object will be destroyed by calling 'delete' on its pointer
648: // (e.g., we assume the pointer was obtained with new)
649: this->createAllocators();
650: if (xx) {
651: this->objPtr = xx;
652: this->refCnt = this->int_allocator->create(1);
653: //this->refCnt = new int(1);
654: this->sz = 0;
655: } else {
656: this->objPtr = NULL;
657: this->refCnt = NULL;
658: this->sz = 0;
659: }
660: }
662: // Work around for thing allocated with an allocator
663: template <class X, typename A>
664: Obj<X,A>::Obj(X *xx, size_type sz){// such an object will be destroyed by the allocator
665: this->createAllocators();
666: if (xx) {
667: this->objPtr = xx;
668: this->refCnt = this->int_allocator->create(1);
669: this->sz = sz;
670: } else {
671: this->objPtr = NULL;
672: this->refCnt = NULL;
673: this->sz = 0;
674: }
675: }
676:
677: template <class X, typename A>
678: Obj<X,A>::Obj(X *_xx, int *_refCnt, size_type _sz) { // This is intended to be private.
679: this->createAllocators();
680: if (!_xx) {
681: throw ALE::Exception("Making an Obj with a NULL objPtr");
682: }
683: this->objPtr = _xx;
684: this->refCnt = _refCnt; // we assume that all refCnt pointers are obtained using an int_allocator
685: (*this->refCnt)++;
686: this->sz = _sz;
687: //if (!this->sz) {
688: // throw ALE::Exception("Making an Obj with zero size");
689: //}
690: }
691:
692: template <class X, typename A>
693: Obj<X,A>::Obj(const Obj& obj) {
694: this->createAllocators();
695: this->objPtr = obj.objPtr;
696: this->refCnt = obj.refCnt;
697: if (obj.refCnt) {
698: (*this->refCnt)++;
699: }
700: this->sz = obj.sz;
701: //if (!this->sz) {
702: // throw ALE::Exception("Making an Obj with zero size");
703: //}
704: }
706: // Destructor
707: template <class X, typename A>
708: Obj<X,A>::~Obj(){
709: this->destroy();
710: this->destroyAllocators();
711: }
713: template <class X, typename A>
714: void Obj<X,A>::createAllocators() {
715: this->handleAllocators(true);
716: }
718: template <class X, typename A>
719: void Obj<X,A>::destroyAllocators() {
720: this->handleAllocators(false);
721: }
723: template <class X, typename A>
724: void Obj<X,A>::handleAllocators(const bool create) {
725: static Allocator_int *s_int_allocator = NULL;
726: static Allocator *s_allocator = NULL;
727: static int s_allocRefCnt = 0;
729: if (create) {
730: if (s_allocRefCnt == 0) {
731: s_int_allocator = new Allocator_int();
732: s_allocator = new Allocator();
733: }
734: s_allocRefCnt++;
735: this->int_allocator = s_int_allocator;
736: this->allocator = s_allocator;
737: } else {
738: if (--s_allocRefCnt == 0) {
739: delete int_allocator;
740: delete allocator;
741: }
742: this->int_allocator = NULL;
743: this->allocator = NULL;
744: }
745: }
747: template <class X, typename A>
748: Obj<X,A>& Obj<X,A>::create(const X& x) {
749: // Destroy the old state
750: this->destroy();
751: // Create the new state
752: this->objPtr = this->allocator->create(x);
753: this->refCnt = this->int_allocator->create(1);
754: this->sz = this->allocator->sz;
755: if (!this->sz) {
756: throw ALE::Exception("Making an Obj with zero size obtained from allocator");
757: }
758: return *this;
759: }
761: template <class X, typename A>
762: void Obj<X,A>::destroy() {
763: if(ALE::getVerbosity() > 3) {
764: #ifdef ALE_USE_DEBUGGING
765: const char *id_name = ALE::getClassName<X>();
767: printf("Obj<X>.destroy: Destroying Obj<%s>", id_name);
768: if (!this->refCnt) {
769: printf(" with no refCnt\n");
770: } else {
771: printf(" with refCnt %d\n", *this->refCnt);
772: }
773: ALE::restoreClassName<X>(id_name);
774: #endif
775: }
776: if (this->refCnt != NULL) {
777: (*this->refCnt)--;
778: if (*this->refCnt == 0) {
779: // If allocator has been used to create an objPtr, as indicated by 'sz', we use the allocator to delete objPtr, using 'sz'.
780: if(this->sz != 0) {
781: #ifdef ALE_USE_DEBUGGING
782: if(ALE::getVerbosity() > 3) {
783: printf(" Calling deallocator on %p with size %d\n", this->objPtr, (int) this->sz);
784: }
785: #endif
786: this->allocator->del(this->objPtr, this->sz);
787: this->sz = 0;
788: }
789: else { // otherwise we use 'delete'
790: #ifdef ALE_USE_DEBUGGING
791: if(ALE::getVerbosity() > 3) {
792: printf(" Calling delete on %p\n", this->objPtr);
793: }
794: #endif
795: if (!this->objPtr) {
796: throw ALE::Exception("Trying to free NULL pointer");
797: }
798: delete this->objPtr;
799: }
800: // refCnt is always created/delete using the int_allocator.
801: this->int_allocator->del(this->refCnt);
802: this->objPtr = NULL;
803: this->refCnt = NULL;
804: }
805: }
806: }
808: // assignment operator
809: template <class X, typename A>
810: Obj<X,A>& Obj<X,A>::operator=(const Obj<X,A>& obj) {
811: if(this->objPtr == obj.objPtr) {return *this;}
812: // Destroy 'this' Obj -- it will properly release the underlying object if the reference count is exhausted.
813: if(this->objPtr) {
814: this->destroy();
815: }
816: // Now copy the data from obj.
817: this->objPtr = obj.objPtr;
818: this->refCnt = obj.refCnt;
819: if(this->refCnt!= NULL) {
820: (*this->refCnt)++;
821: }
822: this->sz = obj.sz;
823: return *this;
824: }
826: // conversion operator, preserves 'this'
827: template<class X, typename A> template<class Y>
828: Obj<X,A>::operator Obj<Y> const() {
829: // We attempt to cast X* objPtr to Y* using dynamic_
830: #ifdef ALE_USE_DEBUGGING
831: if(ALE::getVerbosity() > 1) {
832: printf("Obj<X>::operator Obj<Y>: attempting a dynamic_cast on objPtr %p\n", this->objPtr);
833: }
834: #endif
835: Y* yObjPtr = dynamic_cast<Y*>(this->objPtr);
836: // If the cast failed, throw an exception
837: if(yObjPtr == NULL) {
838: const char *Xname = ALE::getClassName<X>();
839: const char *Yname = ALE::getClassName<Y>();
840: std::string msg("Bad cast Obj<");
841: msg += Xname;
842: msg += "> --> Obj<";
843: msg += Yname;
844: msg += ">";
845: ALE::restoreClassName<X>(Xname);
846: ALE::restoreClassName<X>(Yname);
847: throw BadCast(msg.c_str());
848: }
849: // Okay, we can proceed
850: return Obj<Y>(yObjPtr, this->refCnt, this->sz);
851: }
853: // assignment-conversion operator
854: template<class X, typename A> template<class Y>
855: Obj<X,A>& Obj<X,A>::operator=(const Obj<Y>& obj) {
856: // We attempt to cast Y* obj.objPtr to X* using dynamic_cast
857: X* xObjPtr = dynamic_cast<X*>(obj.objPtr);
858: // If the cast failed, throw an exception
859: if(xObjPtr == NULL) {
860: const char *Xname = ALE::getClassName<X>();
861: const char *Yname = ALE::getClassName<Y>();
862: std::string msg("Bad assignment cast Obj<");
863: msg += Yname;
864: msg += "> --> Obj<";
865: msg += Xname;
866: msg += ">";
867: ALE::restoreClassName<X>(Xname);
868: ALE::restoreClassName<X>(Yname);
869: throw BadCast(msg.c_str());
870: }
871: // Okay, we can proceed with the assignment
872: if(this->objPtr == obj.objPtr) {return *this;}
873: // Destroy 'this' Obj -- it will properly release the underlying object if the reference count is exhausted.
874: this->destroy();
875: // Now copy the data from obj.
876: this->objPtr = xObjPtr;
877: this->refCnt = obj.refCnt;
878: (*this->refCnt)++;
879: this->sz = obj.sz;
880: return *this;
881: }
882:
883: // copy operator (USE WITH CAUTION)
884: template<class X, typename A> template<class Y>
885: Obj<X,A>& Obj<X,A>::copy(const Obj<Y>& obj) {
886: if(this->isNull() || obj.isNull()) {
887: throw(Exception("Copying to or from a null Obj"));
888: }
889: *(this->objPtr) = *(obj.objPtr);
890: return *this;
891: }
894: } // namespace ALE
896: #endif