libshevek
crefptr.hh
1 /* crefptr - cyclic-protected reference counting smart pointers.
2  * Copyright 2009 Bas Wijnen <wijnen@debian.org>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef SHEVEK_CREFPTR_HH
19 #define SHEVEK_CREFPTR_HH
20 
21 #include <list>
22 #include <iostream>
23 #include "error.hh"
24 
25 namespace shevek
26 {
27  // Structure of the refptr:
28  // The object with the data derives from crefbase. This cannot be copied.
29  // Pointers to it are of type crefptr <_T>, where _T is the deriving class.
30  // crefbase contains a list of _ptrdata with pointers to the object.
31  // _ptrdata contains the target object, the pointer which references it and the owner of the reference.
32  // crefptr contains (through _ptr) a list iterator, pointing to its _ptrdata.
33  // crefptr->_data->_ref is always crefptr, and object->_refs[]->target is always object.
34 
36 
39  class crefbase
40  {
41  class _ptr;
42  class _ptrptr;
43  class _objptr;
44  template <typename _T> friend class crefptr;
45 
46  // Internal pointers to crefptrs.
47  class _ptrptr
48  {
49  _ptr *_data;
50  public:
51  _ptr &operator* () { return *_data; }
52  _ptr *operator-> () { return _data; }
53  _ptrptr (_ptr *p) { _data = p; }
54  };
55 
56  // Internal pointers to crefbase objects.
57  class _objptr
58  {
59  crefbase *_data;
60  public:
61  _objptr (crefbase *d) { _data = d; }
62  crefbase &operator* () { return *_data; }
63  crefbase *operator-> () const { return _data; }
64  };
65 
66  // This struct is used internally for storing back references to a pointer.
67  struct _ptrdata
68  {
69  _objptr target; // Link to self, so _ptr only needs to store an iterator.
70  _ptrptr ref; // The referencing pointer.
71  _objptr owner; // The owner of ref.
72  _ptrdata (_objptr t, _ptrptr r, _objptr o) : target (t), ref (r), owner (o) {}
73  };
74 
75  // Base class for crefptr.
76  class _ptr
77  {
78  friend class crefbase;
79  template <typename _T> friend class crefptr;
80  std::list <_ptrdata>::iterator _data;
81  _objptr _target () const { return _data->target; }
82  _objptr &_owner () const { return _data->owner; }
83  // The reference counting machinery.
84  protected:
85  inline _ptr (_objptr owner, _objptr target);
86  inline ~_ptr ();
87  public:
88  inline _ptr (_ptr const &that);
89  inline _ptr &operator= (_ptr const &that);
90  inline void reset ();
91  inline void set_owner (crefbase *owner);
92  };
93 
94  // Object to which null-pointers point. This is needed to store their owners. This object is never deleted because init_done () is never called.
95  static crefbase no_target;
96  // This is set at creation; the object cannot be destroyed until it is reset using init_done ().
97  int _init;
98  // List of pointers referencing this object, with their owner.
99  // This costs more memory than storing the data in _ptr, but _ptr must be as small as possible,
100  // because it is passed around and thus copied a lot.
101  std::list <_ptrdata> _refs;
102  // Use _refs.
103  void _check ();
104  bool _checking;
105  void _add (_ptrptr p, _objptr owner);
106  void _remove (_ptrptr p);
107  // Can't copy refcounted objects.
108  crefbase &operator= (crefbase const &that); // NI
109  crefbase (crefbase const &that); // NI
110  static int dbg_tag;
111 #ifdef DEBUG_CREFBASE
112  std::list <crefbase *>::iterator dbg_iterator;
113  static std::list <crefbase *> dbg_check;
114 #endif
115  protected:
117  crefbase () : _init (0), _checking (false)
118  {
119 #ifdef DEBUG_CREFBASE
120  dbg_check.push_back (this);
121  dbg_iterator = --dbg_check.end ();
122 #endif
123  }
125  virtual ~crefbase ()
126  {
127  if (this == &no_target)
128  return;
129  if (!_refs.empty ())
130  shevek_error ("reference list is not empty");
131 #ifdef DEBUG_CREFBASE
132  dbg_check.erase (dbg_iterator);
133 #endif
134  if (_init == 0)
135  shevek_error ("removing object before init_done was called");
136  if (_init != 1)
137  std::cerr << "Removing object " << this << " which is tagged " << _init << '\n';
138  }
139  public:
141 
146  static int set_default_tag (int tag)
147  {
148  int old = dbg_tag;
149  if (tag)
150  dbg_tag = tag;
151  return old;
152  }
154 
160  void init_done (int code = 0)
161  {
162  if (!code)
163  code = dbg_tag;
164  if (this == &no_target)
165  shevek_error ("calling init_done on no_target");
166  if (_init)
167  shevek_error ("calling init_done more than once");
168  _init = code;
169  if (code != 1)
170  std::cerr << "Tagging object " << this << " with code " << code << '\n';
171  _check ();
172  }
174 
177  static void check (bool fatal = true)
178  {
179 #ifdef DEBUG_CREFBASE
180  unsigned count = 0;
181  for (std::list <crefbase *>::iterator i = dbg_check.begin (); i != dbg_check.end (); ++i)
182  {
183  if (*i == &no_target)
184  continue;
185  if (!(*i)->_init)
186  {
187  shevek_warning (shevek::ostring ("init_done not called before check for %08x", (unsigned)*i));
188  ++count;
189  }
190  else if ((*i)->_init != 1)
191  std::cerr << "Check: object " << *i << ", tagged " << (*i)->_init << " still lives (" << (*i)->_refs.size () << " references)\n";
192  }
193  std::cerr << "checked " << dbg_check.size () << " items\n";
194  if (count && fatal)
195  {
196  shevek_error ("check failed");
197  throw "check failed";
198  }
199 #endif
200  }
201  };
202 
204  template <typename _T> class crefptr : public crefbase::_ptr
205  {
206  public:
208  crefptr (crefbase *target = NULL, crefbase *owner = NULL) : _ptr (owner, target) {}
210  _T &operator* () const { if (_target ().operator-> () == &crefbase::no_target) shevek_error ("dereferencing NULL crefptr"); return reinterpret_cast <_T &> (*_target ()); }
212  _T *operator-> () const { if (_target ().operator-> () == &crefbase::no_target) return NULL; return reinterpret_cast <_T *> (_target ().operator-> ()); }
214  bool operator== (crefptr <_T> const &that) const { return _target ().operator-> () == that._target ().operator-> (); }
216  bool operator!= (crefptr <_T> const &that) const { return _target ().operator-> () != that._target ().operator-> (); }
218  template <typename _R> _R *cast_dynamic () const { return dynamic_cast <_R *> (_target ().operator-> ()); }
220  operator _T * () const { _T *ret = reinterpret_cast <_T *> (_target ().operator-> ()); if (ret == &crefbase::no_target) return NULL; return ret; }
222  crefptr <_T> init (int code = 0)
223  {
224  _target ()->init_done (code);
225  return *this;
226  }
227  };
228 
229  crefbase::_ptr::_ptr (_objptr owner, _objptr target)
230  {
231  if (target.operator-> () == NULL)
232  target = &crefbase::no_target;
233  target->_add (this, owner);
234  }
235 
236  crefbase::_ptr::~_ptr ()
237  {
238  _target ()->_remove (this);
239  }
240 
241  // Using the copy constructor is a problem, because the owner is not given.
242  // So it really shouldn't be used, but it's unacceptable to forbid passing
243  // crefptrs as function arguments. So we assume that the copy constructor
244  // will not be used in other places, and set the owner to NULL.
245  crefbase::_ptr::_ptr (_ptr const &that)
246  {
247  _objptr target = that._target ();
248  target->_add (this, NULL);
249  }
250 
251  crefbase::_ptr &crefbase::_ptr::operator= (_ptr const &that)
252  {
253  _objptr target = that._target ();
254  if (target.operator-> () == _target ().operator-> ())
255  return *this;
256  _objptr owner = _owner ();
257  _target ()->_remove (this);
258  target->_add (this, owner);
259  return *this;
260  }
261 
262  void crefbase::_ptr::reset ()
263  {
264  if (_target ().operator-> () == &crefbase::no_target)
265  return;
266  _objptr owner = _owner ();
267  _target ()->_remove (this);
268  crefbase::no_target._add (this, owner);
269  }
270 
271  void crefbase::_ptr::set_owner (crefbase *owner)
272  {
273  _owner () = owner;
274  _target ()->_check ();
275  }
276 }
277 
278 #endif