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: