libshevek
|
00001 /* crefptr - cyclic-protected reference counting smart pointers. 00002 * Copyright 2009 Bas Wijnen <wijnen@debian.org> 00003 * 00004 * This program is free software: you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation, either version 3 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00016 */ 00017 00018 #ifndef SHEVEK_CREFPTR_HH 00019 #define SHEVEK_CREFPTR_HH 00020 00021 #include <list> 00022 #include <iostream> 00023 #include "error.hh" 00024 00025 namespace shevek 00026 { 00027 // Structure of the refptr: 00028 // The object with the data derives from crefbase. This cannot be copied. 00029 // Pointers to it are of type crefptr <_T>, where _T is the deriving class. 00030 // crefbase contains a list of _ptrdata with pointers to the object. 00031 // _ptrdata contains the target object, the pointer which references it and the owner of the reference. 00032 // crefptr contains (through _ptr) a list iterator, pointing to its _ptrdata. 00033 // crefptr->_data->_ref is always crefptr, and object->_refs[]->target is always object. 00034 00036 00039 class crefbase 00040 { 00041 class _ptr; 00042 class _ptrptr; 00043 class _objptr; 00044 template <typename _T> friend class crefptr; 00045 00046 // Internal pointers to crefptrs. 00047 class _ptrptr 00048 { 00049 _ptr *_data; 00050 public: 00051 _ptr &operator* () { return *_data; } 00052 _ptr *operator-> () { return _data; } 00053 _ptrptr (_ptr *p) { _data = p; } 00054 }; 00055 00056 // Internal pointers to crefbase objects. 00057 class _objptr 00058 { 00059 crefbase *_data; 00060 public: 00061 _objptr (crefbase *d) { _data = d; } 00062 crefbase &operator* () { return *_data; } 00063 crefbase *operator-> () const { return _data; } 00064 }; 00065 00066 // This struct is used internally for storing back references to a pointer. 00067 struct _ptrdata 00068 { 00069 _objptr target; // Link to self, so _ptr only needs to store an iterator. 00070 _ptrptr ref; // The referencing pointer. 00071 _objptr owner; // The owner of ref. 00072 _ptrdata (_objptr t, _ptrptr r, _objptr o) : target (t), ref (r), owner (o) {} 00073 }; 00074 00075 // Base class for crefptr. 00076 class _ptr 00077 { 00078 friend class crefbase; 00079 template <typename _T> friend class crefptr; 00080 std::list <_ptrdata>::iterator _data; 00081 _objptr _target () const { return _data->target; } 00082 _objptr &_owner () const { return _data->owner; } 00083 // The reference counting machinery. 00084 protected: 00085 inline _ptr (_objptr owner, _objptr target); 00086 inline ~_ptr (); 00087 public: 00088 inline _ptr (_ptr const &that); 00089 inline _ptr &operator= (_ptr const &that); 00090 inline void reset (); 00091 inline void set_owner (crefbase *owner); 00092 }; 00093 00094 // Object to which null-pointers point. This is needed to store their owners. This object is never deleted because init_done () is never called. 00095 static crefbase no_target; 00096 // This is set at creation; the object cannot be destroyed until it is reset using init_done (). 00097 int _init; 00098 // List of pointers referencing this object, with their owner. 00099 // This costs more memory than storing the data in _ptr, but _ptr must be as small as possible, 00100 // because it is passed around and thus copied a lot. 00101 std::list <_ptrdata> _refs; 00102 // Use _refs. 00103 void _check (); 00104 bool _checking; 00105 void _add (_ptrptr p, _objptr owner); 00106 void _remove (_ptrptr p); 00107 // Can't copy refcounted objects. 00108 crefbase &operator= (crefbase const &that); // NI 00109 crefbase (crefbase const &that); // NI 00110 static int dbg_tag; 00111 #ifdef DEBUG_CREFBASE 00112 std::list <crefbase *>::iterator dbg_iterator; 00113 static std::list <crefbase *> dbg_check; 00114 #endif 00115 protected: 00117 crefbase () : _init (0), _checking (false) 00118 { 00119 #ifdef DEBUG_CREFBASE 00120 dbg_check.push_back (this); 00121 dbg_iterator = --dbg_check.end (); 00122 #endif 00123 } 00125 virtual ~crefbase () 00126 { 00127 if (this == &no_target) 00128 return; 00129 if (!_refs.empty ()) 00130 shevek_error ("reference list is not empty"); 00131 #ifdef DEBUG_CREFBASE 00132 dbg_check.erase (dbg_iterator); 00133 #endif 00134 if (_init == 0) 00135 shevek_error ("removing object before init_done was called"); 00136 if (_init != 1) 00137 std::cerr << "Removing object " << this << " which is tagged " << _init << '\n'; 00138 } 00139 public: 00141 00146 static int set_default_tag (int tag) 00147 { 00148 int old = dbg_tag; 00149 if (tag) 00150 dbg_tag = tag; 00151 return old; 00152 } 00154 00160 void init_done (int code = 0) 00161 { 00162 if (!code) 00163 code = dbg_tag; 00164 if (this == &no_target) 00165 shevek_error ("calling init_done on no_target"); 00166 if (_init) 00167 shevek_error ("calling init_done more than once"); 00168 _init = code; 00169 if (code != 1) 00170 std::cerr << "Tagging object " << this << " with code " << code << '\n'; 00171 _check (); 00172 } 00174 00177 static void check (bool fatal = true) 00178 { 00179 #ifdef DEBUG_CREFBASE 00180 unsigned count = 0; 00181 for (std::list <crefbase *>::iterator i = dbg_check.begin (); i != dbg_check.end (); ++i) 00182 { 00183 if (*i == &no_target) 00184 continue; 00185 if (!(*i)->_init) 00186 { 00187 shevek_warning (shevek::ostring ("init_done not called before check for %08x", (unsigned)*i)); 00188 ++count; 00189 } 00190 else if ((*i)->_init != 1) 00191 std::cerr << "Check: object " << *i << ", tagged " << (*i)->_init << " still lives (" << (*i)->_refs.size () << " references)\n"; 00192 } 00193 std::cerr << "checked " << dbg_check.size () << " items\n"; 00194 if (count && fatal) 00195 { 00196 shevek_error ("check failed"); 00197 throw "check failed"; 00198 } 00199 #endif 00200 } 00201 }; 00202 00204 template <typename _T> class crefptr : public crefbase::_ptr 00205 { 00206 public: 00208 crefptr (crefbase *target = NULL, crefbase *owner = NULL) : _ptr (owner, target) {} 00210 _T &operator* () const { if (_target ().operator-> () == &crefbase::no_target) shevek_error ("dereferencing NULL crefptr"); return reinterpret_cast <_T &> (*_target ()); } 00212 _T *operator-> () const { if (_target ().operator-> () == &crefbase::no_target) return NULL; return reinterpret_cast <_T *> (_target ().operator-> ()); } 00214 bool operator== (crefptr <_T> const &that) const { return _target ().operator-> () == that._target ().operator-> (); } 00216 bool operator!= (crefptr <_T> const &that) const { return _target ().operator-> () != that._target ().operator-> (); } 00218 template <typename _R> _R *cast_dynamic () const { return dynamic_cast <_R *> (_target ().operator-> ()); } 00220 operator _T * () const { _T *ret = reinterpret_cast <_T *> (_target ().operator-> ()); if (ret == &crefbase::no_target) return NULL; return ret; } 00222 crefptr <_T> init (int code = 0) 00223 { 00224 _target ()->init_done (code); 00225 return *this; 00226 } 00227 }; 00228 00229 crefbase::_ptr::_ptr (_objptr owner, _objptr target) 00230 { 00231 if (target.operator-> () == NULL) 00232 target = &crefbase::no_target; 00233 target->_add (this, owner); 00234 } 00235 00236 crefbase::_ptr::~_ptr () 00237 { 00238 _target ()->_remove (this); 00239 } 00240 00241 // Using the copy constructor is a problem, because the owner is not given. 00242 // So it really shouldn't be used, but it's unacceptable to forbid passing 00243 // crefptrs as function arguments. So we assume that the copy constructor 00244 // will not be used in other places, and set the owner to NULL. 00245 crefbase::_ptr::_ptr (_ptr const &that) 00246 { 00247 _objptr target = that._target (); 00248 target->_add (this, NULL); 00249 } 00250 00251 crefbase::_ptr &crefbase::_ptr::operator= (_ptr const &that) 00252 { 00253 _objptr target = that._target (); 00254 if (target.operator-> () == _target ().operator-> ()) 00255 return *this; 00256 _objptr owner = _owner (); 00257 _target ()->_remove (this); 00258 target->_add (this, owner); 00259 return *this; 00260 } 00261 00262 void crefbase::_ptr::reset () 00263 { 00264 if (_target ().operator-> () == &crefbase::no_target) 00265 return; 00266 _objptr owner = _owner (); 00267 _target ()->_remove (this); 00268 crefbase::no_target._add (this, owner); 00269 } 00270 00271 void crefbase::_ptr::set_owner (crefbase *owner) 00272 { 00273 _owner () = owner; 00274 _target ()->_check (); 00275 } 00276 } 00277 00278 #endif