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: