crefptr.hh
/* crefptr - cyclic-protected reference counting smart pointers.
* Copyright 2009 Bas Wijnen <wijnen@debian.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SHEVEK_CREFPTR_HH
#define SHEVEK_CREFPTR_HH
#include <list>
#include <iostream>
#include "error.hh"
namespace shevek
{
// Structure of the refptr:
// The object with the data derives from crefbase. This cannot be copied.
// Pointers to it are of type crefptr <_T>, where _T is the deriving class.
// crefbase contains a list of _ptrdata with pointers to the object.
// _ptrdata contains the target object, the pointer which references it and the owner of the reference.
// crefptr contains (through _ptr) a list iterator, pointing to its _ptrdata.
// crefptr->_data->_ref is always crefptr, and object->_refs[]->target is always object.
class crefbase
{
class _ptr;
class _ptrptr;
class _objptr;
template <typename _T> friend class crefptr;
// Internal pointers to crefptrs.
class _ptrptr
{
_ptr *_data;
public:
_ptr &operator* () { return *_data; }
_ptr *operator-> () { return _data; }
_ptrptr (_ptr *p) { _data = p; }
};
// Internal pointers to crefbase objects.
class _objptr
{
crefbase *_data;
public:
_objptr (crefbase *d) { _data = d; }
crefbase &operator* () { return *_data; }
crefbase *operator-> () const { return _data; }
};
// This struct is used internally for storing back references to a pointer.
struct _ptrdata
{
_objptr target; // Link to self, so _ptr only needs to store an iterator.
_ptrptr ref; // The referencing pointer.
_objptr owner; // The owner of ref.
_ptrdata (_objptr t, _ptrptr r, _objptr o) : target (t), ref (r), owner (o) {}
};
// Base class for crefptr.
class _ptr
{
friend class crefbase;
template <typename _T> friend class crefptr;
std::list <_ptrdata>::iterator _data;
_objptr _target () const { return _data->target; }
_objptr &_owner () const { return _data->owner; }
// The reference counting machinery.
protected:
inline _ptr (_objptr owner, _objptr target);
inline ~_ptr ();
public:
inline _ptr (_ptr const &that);
inline _ptr &operator= (_ptr const &that);
inline void reset ();
inline void set_owner (crefbase *owner);
};
// Object to which null-pointers point. This is needed to store their owners. This object is never deleted because init_done () is never called.
static crefbase no_target;
// This is set at creation; the object cannot be destroyed until it is reset using init_done ().
int _init;
// List of pointers referencing this object, with their owner.
// This costs more memory than storing the data in _ptr, but _ptr must be as small as possible,
// because it is passed around and thus copied a lot.
std::list <_ptrdata> _refs;
// Use _refs.
void _check ();
bool _checking;
void _add (_ptrptr p, _objptr owner);
void _remove (_ptrptr p);
// Can't copy refcounted objects.
crefbase &operator= (crefbase const &that); // NI
crefbase (crefbase const &that); // NI
static int dbg_tag;
#ifdef DEBUG_CREFBASE
std::list <crefbase *>::iterator dbg_iterator;
static std::list <crefbase *> dbg_check;
#endif
protected:
crefbase () : _init (0), _checking (false)
{
#ifdef DEBUG_CREFBASE
dbg_check.push_back (this);
dbg_iterator = --dbg_check.end ();
#endif
}
virtual ~crefbase ()
{
if (this == &no_target)
return;
if (!_refs.empty ())
shevek_error ("reference list is not empty");
#ifdef DEBUG_CREFBASE
dbg_check.erase (dbg_iterator);
#endif
if (_init == 0)
shevek_error ("removing object before init_done was called");
if (_init != 1)
std::cerr << "Removing object " << this << " which is tagged " << _init << '\n';
}
public:
static int set_default_tag (int tag)
{
int old = dbg_tag;
if (tag)
dbg_tag = tag;
return old;
}
void init_done (int code = 0)
{
if (!code)
code = dbg_tag;
if (this == &no_target)
shevek_error ("calling init_done on no_target");
if (_init)
shevek_error ("calling init_done more than once");
_init = code;
if (code != 1)
std::cerr << "Tagging object " << this << " with code " << code << '\n';
_check ();
}
static void check (bool fatal = true)
{
#ifdef DEBUG_CREFBASE
unsigned count = 0;
for (std::list <crefbase *>::iterator i = dbg_check.begin (); i != dbg_check.end (); ++i)
{
if (*i == &no_target)
continue;
if (!(*i)->_init)
{
shevek_warning (shevek::ostring ("init_done not called before check for %08x", (unsigned)*i));
++count;
}
else if ((*i)->_init != 1)
std::cerr << "Check: object " << *i << ", tagged " << (*i)->_init << " still lives (" << (*i)->_refs.size () << " references)\n";
}
std::cerr << "checked " << dbg_check.size () << " items\n";
if (count && fatal)
{
shevek_error ("check failed");
throw "check failed";
}
#endif
}
};
template <typename _T> class crefptr : public crefbase::_ptr
{
public:
crefptr (crefbase *target = NULL, crefbase *owner = NULL) : _ptr (owner, target) {}
_T &operator* () const { if (_target ().operator-> () == &crefbase::no_target) shevek_error ("dereferencing NULL crefptr"); return reinterpret_cast <_T &> (*_target ()); }
_T *operator-> () const { if (_target ().operator-> () == &crefbase::no_target) return NULL; return reinterpret_cast <_T *> (_target ().operator-> ()); }
bool operator== (crefptr <_T> const &that) const { return _target ().operator-> () == that._target ().operator-> (); }
bool operator!= (crefptr <_T> const &that) const { return _target ().operator-> () != that._target ().operator-> (); }
template <typename _R> _R *cast_dynamic () const { return dynamic_cast <_R *> (_target ().operator-> ()); }
operator _T * () const { _T *ret = reinterpret_cast <_T *> (_target ().operator-> ()); if (ret == &crefbase::no_target) return NULL; return ret; }
crefptr <_T> init (int code = 0)
{
_target ()->init_done (code);
return *this;
}
};
crefbase::_ptr::_ptr (_objptr owner, _objptr target)
{
if (target.operator-> () == NULL)
target = &crefbase::no_target;
target->_add (this, owner);
}
crefbase::_ptr::~_ptr ()
{
_target ()->_remove (this);
}
// Using the copy constructor is a problem, because the owner is not given.
// So it really shouldn't be used, but it's unacceptable to forbid passing
// crefptrs as function arguments. So we assume that the copy constructor
// will not be used in other places, and set the owner to NULL.
crefbase::_ptr::_ptr (_ptr const &that)
{
_objptr target = that._target ();
target->_add (this, NULL);
}
crefbase::_ptr &crefbase::_ptr::operator= (_ptr const &that)
{
_objptr target = that._target ();
if (target.operator-> () == _target ().operator-> ())
return *this;
_objptr owner = _owner ();
_target ()->_remove (this);
target->_add (this, owner);
return *this;
}
void crefbase::_ptr::reset ()
{
if (_target ().operator-> () == &crefbase::no_target)
return;
_objptr owner = _owner ();
_target ()->_remove (this);
crefbase::no_target._add (this, owner);
}
void crefbase::_ptr::set_owner (crefbase *owner)
{
_owner () = owner;
_target ()->_check ();
}
}
#endif
shevek::ostring
shevek::ostring is a C++ version of printf.
Definition: iostring.hh:322
shevek::crefptr
Keep a pointer to an object derived from crefbase.
Definition: crefptr.hh:204
shevek::crefptr::operator->
_T * operator->() const
Dereference the pointer.
Definition: crefptr.hh:212
shevek::crefbase::set_default_tag
static int set_default_tag(int tag)
Set the default tag for when init_done is called.
Definition: crefptr.hh:146
shevek::crefptr::operator*
_T & operator*() const
Dereference the pointer.
Definition: crefptr.hh:210
shevek
Definition: args.hh:52
shevek::crefbase::check
static void check(bool fatal=true)
Check if all objects have called init_done.
Definition: crefptr.hh:177
shevek::crefptr::crefptr
crefptr(crefbase *target=NULL, crefbase *owner=NULL)
Create a new pointer. If this pointer is stored inside a crefbase-derived object, make sure to set th...
Definition: crefptr.hh:208
shevek::crefbase::init_done
void init_done(int code=0)
After calling this, the object is destroyed without references.
Definition: crefptr.hh:160
shevek::crefptr::cast_dynamic
_R * cast_dynamic() const
Create a new pointer from this one, up- or downcast. Normally, this is used to fill a new crefptr...
Definition: crefptr.hh:218
shevek::crefbase
Circular-dependancy-protected reference-counting object base class.
Definition: crefptr.hh:39
shevek::crefptr::operator!=
bool operator!=(crefptr< _T > const &that) const
Test if two pointers don't refer to the same object.
Definition: crefptr.hh:216
shevek::crefbase::crefbase
crefbase()
Constructor, which is called when an object is created.
Definition: crefptr.hh:117
shevek::crefbase::~crefbase
virtual ~crefbase()
Virtual destructor, which does nothing except allowing derived class to have a virtual destructor...
Definition: crefptr.hh:125
shevek::crefptr::init
crefptr< _T > init(int code=0)
Allow the pointer to be destroyed. See crefbase::init_done for details.
Definition: crefptr.hh:222
shevek::crefptr::operator==
bool operator==(crefptr< _T > const &that) const
Test if two pointers refer to the same object.
Definition: crefptr.hh:214