1: 
   2: 
   3: /*************
   4: *            *
   5: *  SENTINEL  *
   6: *            *
   7: *************/
   8: 
   9: 
  10: #ifndef WWWI_MIND_DEBUGPTR_H 
  11: #define WWWI_MIND_DEBUGPTR_H 
  12: #ifdef USE_DEBUG_POINTERS 
  13: 
  14: 
  15: /**********************
  16: *                     *
  17: *  COMPILER INCLUDES  *
  18: *                     *
  19: **********************/
  20: 
  21: 
  22: #include <cassert> 
  23: #include <cstdlib> 
  24: #include <iostream> 
  25: #include <map> 
  26: #include <typeinfo> 
  27: 
  28: 
  29: /*********************
  30: *                    *
  31: *  PROJECT INCLUDES  *
  32: *                    *
  33: *********************/
  34: 
  35: 
  36: #include "wwwi/mutex.h" 
  37: #include "wwwi/mutexholder.h" 
  38: #include "wwwi/singleton.h" 
  39: 
  40: 
  41: using std::abort;
  42: using std::cerr;
  43: using std::endl;
  44: using std::map;
  45: using WWWI::GetSingleton;
  46: using WWWI::Mutex;
  47: using WWWI::MutexHolder;
  48: 
  49: 
  50: /*****************
  51: *                *
  52: *  CLASS REFMGR  *
  53: *                *
  54: *****************/
  55: 
  56: 
  57: /*
  58:  *  The RefMgr class is used as a singleton to coordinate references to a 
  59:  *  pointer.  If the number of references drops to 0 before the pointer is
  60:  *  deleted, abort() will be called to generate a core dump.  At program
  61:  *  exit, a summary of allocations of objects of this type will be printed, 
  62:  *  along with a list of unfreed objects (if any).  One RefMgr class is 
  63:  *  constructed for each type of object being tracked.
  64:  *
  65:  *  Methods
  66:  *
  67:  *    RefMgr(void)
  68:  *      Invoked by GetSingleton, the constructor initializes the new/delete
  69:  *      counts for its specified object type.
  70:  *
  71:  *    Delete(const T *ci_tp)
  72:  *      This method is called to indicate that the given pointer is being 
  73:  *      deleted.  If the reference count is zero (or less), abort() is called,
  74:  *      indicating that the object has been freed twice.  This method does
  75:  *      not actually delete the object.
  76:  *
  77:  *    Down(const T *ci_tp)
  78:  *      This method decrements the reference count.  If the reference count
  79:  *      reaches zero, it means that no pointers remain that can free the
  80:  *      object.  abort() is called and a debugger can be used to 
  81:  *      investigate the call stack and find the area of code where the pointer
  82:  *      was discarded.
  83:  *
  84:  *    Up(const T *ci_tp)
  85:  *      This method increases the reference count by one, or begins reference
  86:  *      counting if this is the first use of a pointer.
  87:  *
  88:  *    ~RefMgr()
  89:  *      The destructor prints statistics about how many objects of this type
  90:  *      were created and destroyed.  If any remain unallocated, they will
  91:  *      be enumerated.  Ordinarily there are none, as unreferenced objects
  92:  *      should trigger an abort() when the last reference is lost, but 
  93:  *      circular references and other obscure circumstances can occasionally 
  94:  *      slip past.
  95:  *
  96:  *  Properties
  97:  *
  98:  *    RefMap m_fc
  99:  *      A RefMap is typedef'd to mean a map<const T*,int>.  This map holds
 100:  *      all the allocated pointers and their associated reference counts.
 101:  *      TODO: Where supported, a hash_map would give better performance.
 102:  *
 103:  *    unsigned m_uNew
 104:  *      Keeps track of the number of times objects of this type have been
 105:  *      created.
 106:  *
 107:  *    unsigned m_uDelete
 108:  *      Used to keep track of the number of times objects of this type have
 109:  *      been destroyed.
 110:  */
 111: 
 112: 
 113: template <class T> class RefMgr {
 114: typedef map<const T*,int> RefMap;
 115: public:
 116:   RefMgr(void) {
 117:     MutexHolder mh(&m_mx);
 118:     cerr << "Initializing map for " << typeid(T).name() << endl;
 119:     m_uNew = 0;
 120:     m_uDelete = 0;
 121:   }
 122:   void Up(const T *i_tp) {
 123:     MutexHolder mh(&m_mx);
 124:     if (i_tp==NULL) return;
 125:     typename RefMap::iterator fci;
 126:     fci = m_fc.find(i_tp);
 127:     if (fci==m_fc.end()) { m_fc[i_tp] = 1; m_uNew++; }
 128:     else {
 129:       (*fci).second++;
 130:       if ((*fci).second==1) m_uNew++; 
 131:     }
 132:   }
 133:   void Down(const T *i_tp) {
 134:     MutexHolder mh(&m_mx);
 135:     if (i_tp==NULL) return;
 136:     typename RefMap::iterator fci;
 137:     fci = m_fc.find(i_tp);
 138:     if (fci==m_fc.end()) abort();
 139:     if ((*fci).second<=0) abort(); 
 140:     (*fci).second--;
 141:     if ((*fci).second==0) abort(); // No references remain.
 142:   }
 143:   void Delete(const T *i_tp) {
 144:     MutexHolder mh(&m_mx);
 145:     if (i_tp==NULL) abort();
 146:     typename RefMap::iterator fci;
 147:     fci = m_fc.find(i_tp);
 148:     if (fci==m_fc.end()) abort();
 149:     if ((*fci).second<=0) abort();
 150:     (*fci).second--;
 151:     if ((*fci).second>0) cerr << typeid(T).name() << " deleted at " << i_tp << " with " << (*fci).second << " references remaining." << endl;
 152:     m_uDelete++;
 153:   }
 154:   ~RefMgr(void) {
 155:     MutexHolder mh(&m_mx);
 156:     typename RefMap::iterator fci;
 157:     cerr << typeid(T).name() << " created " << m_uNew << " deleted " << m_uDelete << endl;
 158:     for (fci=m_fc.begin(); fci!=m_fc.end(); ++fci) {
 159:       if ((*fci).second!=0) {
 160:         cerr << typeid(T).name() << " at " << ((*fci).first)
 161:              << " was not freed." << endl;
 162:       }
 163:     }
 164:   }
 165:   
 166: protected:
 167:   Mutex  m_mx;
 168:   RefMap m_fc;
 169:   unsigned m_uNew;
 170:   unsigned m_uDelete;
 171: };
 172: 
 173: 
 174: /*******************
 175: *                  *
 176: *  CLASS DEBUGPTR  *
 177: *                  *
 178: *******************/
 179: 
 180: /*
 181:  *  The DebugPtr<T> class is a limited form of smart pointer used to track 
 182:  *  pointers to objects of type T.  It uses a matching RefMgr<T> singleton
 183:  *  to store the statistics.
 184:  *
 185:  *  Methods
 186:  *
 187:  *    DebugPtr(void)
 188:  *    DebugPtr(T *i_tp)
 189:  *    DebugPtr(const DebugPtr<T> &ci_tdr)
 190:  *      A DebugPtr<T> can be constructed in one of three ways.  By default, 
 191:  *      it is initialized to NULL.  It can also be initialized with a pointer
 192:  *      to a T object, such as that returned by "new T".  Lastly, it can be
 193:  *      copied from an existing DebugPtr<T> object.  The second and third forms
 194:  *      increment the reference count on the pointer.  In the second case, this
 195:  *      generally means setting it to 1.
 196:  *
 197:  *    operator->(void) const
 198:  *    operator->(void)
 199:  *      The DebugPtr<T> class overloads its -> operator to emulate behavior 
 200:  *      of the contained pointer.  The const overload returns a const pointer.
 201:  *
 202:  *    T &operator*(void) const
 203:  *      The DebugPtr<T> class also overrides its * operator to emulate the
 204:  *      behavior of the contained pointer.  Note that although this method
 205:  *      is const in terms of the DebugPtr and its contents, the reference
 206:  *      returned is not const and may be used to modify the underlying data.
 207:  *    
 208:  *    operator=(T *i_tp)
 209:  *    operator=(const DebugPtr<T> &ci_tdpr)
 210:  *      The = operator is overloaded to allow assignment.  A DebugPtr<T> can
 211:  *      receive an assignment from either a pointer to a T object or from 
 212:  *      another DebugPtr<T>.  In either case, the reference count on the current
 213:  *      value is decremented while the count on the new value is incremented.
 214:  *
 215:  *    operator==(const T *ci_vp) const
 216:  *    operator==(const DebugPtr<T> &ci_tdpr) const
 217:  *    operator==(const DebugPtr<const T> &ci_tdpr) const
 218:  *      The comparison operator (==) is overloaded to compare the underlying
 219:  *      pointer rather than the DebugPtr<T> object itself.  
 220:  *
 221:  *    operator!=(const T *i_tp) const
 222:  *    operator==(const DebugPtr<T> &ci_tdpr) const
 223:  *    operator==(const DebugPtr<const T> &ci_tdpr) const
 224:  *      Returns true if the == equivalent is false.
 225:  *
 226:  *    Delete(void)
 227:  *      This method is called by SoftDelete() to free the DebugPtr<T> and
 228:  *      its contents.  Since C++ operator semantics do not allow overloading
 229:  *      the direct deletion of a non-pointer type, objects that are or are
 230:  *      potentially pointed to by DebugPtr<T> are usually freed with 
 231:  *      SoftDelete().  This alerts the RefMgr<T> that the deletion is occuring
 232:  *      and deletes the underlying pointer.
 233:  *
 234:  *    Get(void)
 235:  *      This method returns the member pointer directly for those situations
 236:  *      that accept no imitations.
 237:  *
 238:  *    Set(const T *i_tp)
 239:  *      This method is used internally by the constructors and the assignment
 240:  *      operator for the common code.  Called externally, it is equivalent to
 241:  *      assignment.
 242:  *
 243:  *    ~DebugPtr()
 244:  *      The destructor decrements the reference count on the contained pointer.
 245:  *      See RefMgr::Down() for a discussion of what happens when the reference
 246:  *      count reaches zero without an explicit delete.
 247:  *      
 248:  *  Properties
 249:  *
 250:  *    T *m_tp
 251:  *      The pointer around which the entire object revolves.
 252:  *
 253:  *  Note: the unquoted word "xfactor" appears in certain methods below.  This
 254:  *  indicates that although the method was coded, it has never been instantiated
 255:  *  by the compiler and thus has never been proven to work correctly.  It should
 256:  *  therefore be treated as something of an unknown. 
 257:  */
 258: 
 259: template <class T> class DebugPtr {
 260: typedef RefMgr<T> RefMgrT;
 261: public:
 262:   DebugPtr(void) { m_tp = NULL; }
 263:   DebugPtr(T *i_tp) { m_tp = NULL; this->Set(i_tp); }
 264:   DebugPtr(const DebugPtr<T> &ci_tdr) { m_tp=NULL; this->Set(ci_tdr.m_tp); }
 265:   inline const T *operator->(void) const { return m_tp; }
 266:   inline T       *operator->(void) { return m_tp; }
 267:   inline T       &operator*(void) const { return *m_tp; }
 268:   inline void     operator=(T *i_tp) { this->Set(i_tp); }
 269:   inline void     operator=(const DebugPtr<T> &ci_tdpr) { 
 270:     this->Set(ci_tdpr.m_tp); 
 271:   }
 272:   inline bool     operator==(const T *i_tp) const { return (m_tp==i_tp); }
 273:   inline bool     operator==(const DebugPtr<T> &ci_tdpr) const { 
 274:     return (m_tp==i_pv); 
 275:     xfactor;  
 276:   }
 277:   inline bool     operator==(const DebugPtr<const T> &ci_tdpr) const { 
 278:     return (m_tp==ci_tdpr.m_tp); 
 279:     xfactor;  
 280:   }
 281:   inline bool     operator!=(const T *ci_tp) const { return (m_tp!=ci_tp); }
 282:   inline bool     operator!=(const DebugPtr<T> &ci_tdpr) const { 
 283:     return (m_tp!=ci_tdpr.m_tp); 
 284:     xfactor; 
 285:   }
 286:   inline bool     operator!=(const DebugPtr<const T> &ci_tdpr) const { 
 287:     return (m_tp!=ci_tdpr.m_tp); 
 288:     xfactor; 
 289:   }
 290:   inline void     Delete(void) {
 291:     GetSingleton<RefMgrT>()->Delete(m_tp);
 292:     delete m_tp;
 293:     m_tp = NULL;
 294:   }   
 295:   inline const T* Get(void) const { return m_tp; }
 296:   inline void Set(T *i_tp) {
 297:     RefMgrT *rgp = GetSingleton<RefMgrT>();
 298:     rgp->Down(m_tp);
 299:     m_tp = i_tp;
 300:     rgp->Up(m_tp);
 301:   }
 302:   ~DebugPtr(void) {
 303:     RefMgrT *rgp = GetSingleton<RefMgrT>();
 304:     rgp->Down(m_tp);
 305:     m_tp = NULL;
 306:   }
 307: protected:
 308:   mutable T *m_tp;
 309: };
 310: 
 311: 
 312: /*
 313:  *  DebugPtr<const T> is a partial template specialization of DebugPtr<T>.
 314:  *
 315:  *  DebugPtr<const T> has all the same methods as DebugPtr<T> with the addition
 316:  *  of methods to support intake of pointers from both DebugPtr<T> and 
 317:  *  DebugPtr<const T> types for construction and assignment.  This models
 318:  *  the behavior of real pointers, where a pointer to an object can
 319:  *  be assigned to a pointer to a const object, but not vice-versa.
 320:  *
 321:  *  This is not necessarily the easiest way to model the desired behavior.  
 322:  *  However, someone recently asked me what partial template specialization
 323:  *  might be useful for "in the real world."
 324:  */
 325: 
 326: 
 327: template <class T> class DebugPtr<const T> {
 328: typedef RefMgr<T> RefMgrT;
 329: public:
 330:   DebugPtr(void) { cm_tp = NULL; }
 331:   DebugPtr(const DebugPtr<const T> &ci_tdr) { 
 332:     cm_tp = NULL; 
 333:     this->Set(ci_tdr.Get()); 
 334:   }
 335:   DebugPtr(const DebugPtr<T> i_tdp) { 
 336:     cm_tp = NULL; 
 337:     this->Set(i_tdp.Get());
 338:   }
 339:   DebugPtr(const T *ci_tp) { 
 340:     cm_tp = NULL; 
 341:     this->Set(ci_tp); 
 342:   }
 343:   inline void Set(const T *ci_tp) { 
 344:     RefMgrT *rgp = GetSingleton<RefMgrT>();
 345:     rgp->Down(cm_tp);
 346:     cm_tp = ci_tp;
 347:     rgp->Up(cm_tp);
 348:   }
 349:   inline const T *operator->(void) const { return cm_tp; }
 350:   inline const T &operator*(void) const { return *cm_tp; }
 351:   inline void     operator=(const T *i_tp) { this->Set(i_tp); }
 352:   inline void     operator=(const DebugPtr<const T> &ci_tdp) { 
 353:     this->Set(ci_tdp.cm_tp);
 354:   }
 355:   inline bool     operator==(const void* ci_pv) const { return (cm_tp==ci_pv); }
 356:   inline bool     operator==(const DebugPtr<T> &ci_tdpr) const { 
 357:     return (cm_tp==ci_tdpr.m_tp);  
 358:     xfactor;  
 359:   }
 360:   inline bool     operator==(const DebugPtr<const T> &ci_tdpr) const { 
 361:     return (cm_tp==ci_tdpr.cm_tp); 
 362:     xfactor;   
 363:   }
 364:   inline bool     operator!=(const T* ci_tp) const { return (cm_tp!=ci_tp); }
 365:   inline bool     operator!=(const DebugPtr<T> &ci_tdpr) const { 
 366:     return (cm_tp!=ci_tdpr.m_tp);  
 367:     xfactor; 
 368:   }
 369:   inline bool     operator!=(const DebugPtr<const T> &ci_tdpr) const { 
 370:     return (cm_tp!=ci_tdpr.cm_tp); 
 371:   }
 372:   inline const T* Get(void) const { return cm_tp; }
 373:   ~DebugPtr(void) {
 374:     RefMgrT *rgp = GetSingleton<RefMgrT>();
 375:     rgp->Down(cm_tp);
 376:   }
 377: protected:
 378:   const T *cm_tp;
 379: };
 380:     
 381: 
 382: #endif  // USE_DEBUG_POINTERS 
 383: #endif  // WWWI_MIND_DEBUGPTR_H 
 384: 
 385: 
 386: