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