libshevek

- Main Page
- Classes
- Files

- File List

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