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