libshevek
src/crefptr.hh
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