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