/* -*- c++ -*- C++ smart pointer classes This file is part of the dpp library of C++ template classes doc: http://diaxen.ssji.net/dpp/index.html repo: https://www.ssji.net/svn/projets/trunk/libdpp This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . (c) 2008-2011 Alexandre Becoulet */ #ifndef DPP_REFS_HH_ #define DPP_REFS_HH_ #include #include #include /** @file @module{Smart pointer} */ // #define _DPP_USE_GCC_ATOMIC namespace dpp { template class ref; template class const_ref; template class clone_ref; template class ref_base; /** @internal @module{Smart pointer} */ template class ref_ { template friend class ref_; template friend class ref; template friend class const_ref; template friend class clone_ref; /** @deprecated Dynamically allocate and construct object of given type with passed constructor arguments. */ #define DPP_REFNEW(type, ...) \ (dpp::ref::create(__VA_ARGS__)) public: /** @This set the internal pointer to 0. */ void invalidate() { if (_obj) _drop(); _obj = 0; } /** @This tests if @ref ref points to a valid object. */ bool valid() const { return _obj != 0; } X & operator*() const { assert(_obj); return *_obj; } X * operator->() const { assert(_obj); return _obj; } /** @This returns ref internal object pointer */ X * ptr() const { return _obj; } /** @This returns object references count */ int count() const { return _obj ? _obj->_ref_count : 0; } /** @This tests if pointed objects are the same */ bool operator==(const ref_ &r) const { return _obj == r._obj; } /** @This tests if pointed objects are not the same */ bool operator!=(const ref_ &r) const { return _obj != r._obj; } /** @This tests if pointed objects are the same */ bool operator==(const X *p) const { return _obj == p; } /** @This tests if pointed objects are the same */ bool operator!=(const X *p) const { return _obj != p; } /** @This returns a @ref ref to a new dynamically allocated copy of object. Object is deep copyed, new object has the same derived type as source object. While this is not need for objects dynamically allocated with the @ref ref::create function, other objects have to call the @ref ref_base::set_clone function from their constructor to make the @ref clone function work. The @ref clone_ref class can be used for automatic cloning on smart pointer copy. @example test/test_ref.cc:clone */ ref clone() const { if (!_obj) return ref(); return ref(_obj->template _clone()); } private: /** @internal */ void assign(const X &r) { if (_obj) _drop(); _obj = &r; if (_obj) _inc(); } /** @internal */ void assign(const ref_ &r) { if (_obj) _drop(); _obj = r._obj; if (_obj) _inc(); } /** @internal */ void _inc() const { _obj->ref_inc(); } /** @internal */ void _drop() const { _obj->ref_drop(); } /** @internal */ X *_obj; }; /** @short Smart pointer class @module{Smart pointer} @header dpp/ref @main @order 3 @This implements an object reference class, it may hold a reference to objects of type @tt X which inherit from the @ref ref_base class. This class can hold references to both statically and dynamically allocated objects. When the object has been dynamically allocated using the @ref create function, it will be destroyed when the last reference to the object is dropped. @see {const_ref, ref_base} */ template class ref : public ref_ { template friend class ref_; template friend class ref; template friend class clone_ref; typedef ref_ base; explicit ref(X * obj) { base::_obj = obj; } public: /** @multiple @This dynamically allocates and construct object. Parameters are passed to constructor. */ static ref create() { X *obj = new X(); obj->template _set_clone(); obj->_dynamic = true; obj->_ref_count = 1; return ref(obj); } template static ref create(const A0 &a0) { X *obj = new X(a0); obj->template _set_clone(); obj->_dynamic = true; obj->_ref_count = 1; return ref(obj); } template static ref create(const A0 &a0, const A1 &a1) { X *obj = new X(a0, a1); obj->template _set_clone(); obj->_dynamic = true; obj->_ref_count = 1; return ref(obj); } template static ref create(const A0 &a0, const A1 &a1, const A2 &a2) { X *obj = new X(a0, a1, a2); obj->template _set_clone(); obj->_dynamic = true; obj->_ref_count = 1; return ref(obj); } template static ref create(const A0 &a0, const A1 &a1, const A2 &a2, const A3 &a3) { X *obj = new X(a0, a1, a2, a3); obj->template _set_clone(); obj->_dynamic = true; obj->_ref_count = 1; return ref(obj); } template static ref create(const A0 &a0, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4) { X *obj = new X(a0, a1, a2, a3, a4); obj->template _set_clone(); obj->_dynamic = true; obj->_ref_count = 1; return ref(obj); } template static ref create(const A0 &a0, const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5) { X *obj = new X(a0, a1, a2, a3, a4, a5); obj->template _set_clone(); obj->_dynamic = true; obj->_ref_count = 1; return ref(obj); } /** @This performs a dynamic cast to @ref ref of given type */ template ref dynamiccast() const { T *r = dynamic_cast(base::_obj); if (r) base::_inc(); return ref(r); } /** @This performs a static cast to @ref ref of given type */ template ref staticcast() const { if (base::_obj) base::_inc(); return ref(static_cast(base::_obj)); } /** Construct an empty ref */ ref() { base::_obj = 0; } /** Construct ref from ref */ ref(const ref &r) { if ((base::_obj = r._obj)) base::_inc(); } /** Construct ref from derived class ref */ template ref(const ref_ &r) { if ((base::_obj = r._obj)) base::_inc(); } /** Construct ref from object reference */ ref(X &obj) { base::_obj = &obj; base::_inc(); } /** Drop a ref */ ~ref() { if (base::_obj) base::_drop(); } /** Initialize ref from ref */ ref & operator=(const ref &r) { base::assign(r); return *this; } /** Initialize ref from object reference */ ref & operator=(X & obj) { base::assign(obj); return *this; } }; /** @short Const smart pointer class @module{Smart pointer} @header dpp/ref @main @order 2 @This class is the same as the @ref ref class but hold a @b const pointer to the object. @see {ref, ref_base} */ template class const_ref : public ref_ { template friend class ref_; typedef ref_ base; explicit const_ref(const X * obj) { base::_obj = obj; } public: /** Construct an empty ref */ const_ref() { base::_obj = 0; } /** Construct const ref from const ref */ const_ref(const const_ref &r) { if ((base::_obj = r._obj)) base::_inc(); } /** Construct const ref from derived class ref */ template const_ref(const ref_ &r) { if ((base::_obj = r._obj)) base::_inc(); } /** Construct const ref from derived class const ref */ template const_ref(const ref_ &r) { if ((base::_obj = r._obj)) base::_inc(); } /** Construct ref from const object reference */ const_ref(const X &obj) { base::_obj = &obj; base::_inc(); } /** Drop a ref */ ~const_ref() { if (base::_obj) base::_drop(); } /** @This performs a dynamic cast to @ref const_ref of given type */ template const_ref dynamiccast() const { const T *r = dynamic_cast(base::_obj); if (r) base::_inc(); return const_ref(r); } /** @This performs a static cast to @ref const_ref of given type */ template const_ref staticcast() const { if (base::_obj) base::_inc(); return const_ref(static_cast(base::_obj)); } /** Initialize const ref from const ref */ const_ref & operator=(const const_ref &r) { base::assign(r); return *this; } /** Initialize ref from object reference */ const_ref & operator=(const X & obj) { base::assign(obj); return *this; } }; /** @short Automatic cloning smart pointer class @module{Smart pointer} @header dpp/ref @main @order 1 @This implements a special kind of smart pointer which clones the pointed object when copied. It is intended to be used for class members when the expected behavior is to clone the pointed object along with the reference holder object. The copy constructor and copy assignment operator will clone the associated object instead of just copying the pointer. Constructors and assignment operators which take other kind of references don't perform clone. @example test/test_ref.cc:clone1 @see {ref, ref_base} */ template class clone_ref : public ref_ { template friend class ref_; typedef ref_ base; public: /** Construct an empty ref */ clone_ref() { base::_obj = 0; } /** Construct ref from derived class ref */ template clone_ref(const ref_ &r) { base::_obj = r._obj; if (base::_obj) base::_inc(); } /** Construct clone_ref and clone pointed object */ clone_ref(const clone_ref &r) { base::_obj = r._obj ? r._obj->template _clone() : 0; } /** Construct clone_ref and clone pointed object */ template clone_ref(const clone_ref &r) { base::_obj = r._obj ? r._obj->template _clone() : 0; } /** Construct clone_ref from object reference */ clone_ref(X &obj) { base::_obj = &obj; base::_inc(); } /** Drop a clone_ref */ ~clone_ref() { if (base::_obj) base::_drop(); } /** @This performs a dynamic cast to @ref ref of given type */ template ref dynamiccast() const { T *r = dynamic_cast(base::_obj); if (r) base::_inc(); return ref(r); } /** @This performs a static cast to @ref ref of given type */ template ref staticcast() const { if (base::_obj) base::_inc(); return ref(static_cast(base::_obj)); } /** Initialize clone_ref by cloning object */ clone_ref & operator=(const clone_ref &r) { if (base::_obj) base::_drop(); base::_obj = r._obj ? r._obj->_clone() : 0; return *this; } /** Initialize ref from ref */ clone_ref & operator=(const ref &r) { base::assign(r); return *this; } /** Initialize ref from object reference */ clone_ref & operator=(X & obj) { base::assign(obj); return *this; } }; /** @internal @module{Smart pointer} */ template class ref_base_ { template friend class ref; template void _set_clone() { /* not cloneable, no clone callback registered here */ } }; /** @internal @module{Smart pointer} This class contains cloning code. */ template class ref_base_ { template friend class ref_; template friend class ref; template friend class clone_ref; template friend class ref_base; ref_base_() : _ref_clone(0) { } /** Clone object pointed by ref */ template D * _clone() const { assert(_ref_clone || !"no clone handler defined for this object"); return static_cast(_ref_clone(static_cast(this))); } template static X * _clone_hndl(const X *x) { assert(typeid(D) == typeid(*x) && "clone handler does not match actual object type"); const D * src = static_cast(x); D * obj = new D(*src); obj->_ref_clone = src->_ref_clone; obj->_dynamic = true; obj->_ref_count = 1; obj->cloned(*src); return obj; } /** set specialized clone callback */ template void _set_clone() { _ref_clone = &_clone_hndl; } /** pointer to clone function specialized for _derived_ class */ X * (*_ref_clone)(const X *); }; /** @short Reference counting object base class @module{Smart pointer} @header dpp/ref @showvalue @This is the referenced object base class, any class which inherits from this class can be pointed to by a @ref ref smart pointer. @tt X template parameter must be the same as inheriting class. The @tt cloneable parameter must be set to true in order to use the @ref ref::clone method. The default value is @tt false to support classes with no copy constructor and optimize pointer size. When the DPP_REF_LIVE_REFS_ASSERT macro is defined, the base class destructor assert that no more live reference to the object does exist. */ template class ref_base : public ref_base_ { template friend class ref_; template friend class ref; template friend class const_ref; template friend class ref_base_; public: ref_base() : _ref_count(0), _dynamic(0) { } ref_base(const ref_base &r) : _ref_count(0), _dynamic(0) { } ref_base & operator=(const ref_base &r) { return *this; } virtual ~ref_base() { #ifdef DPP_REF_LIVE_REFS_ASSERT assert(_ref_count == 0 || !"Can not destruct object which has live references"); #endif } /** Setup a clone handler for this object. This function may be called from derived class constructor to make objects cloneable. Objects which are dynamically allocated using the @ref ref::create function do not require this call. */ template void set_clone() { ref_base_::template _set_clone(); } /** @This is called when this object has just been cloned. The call is performed on most derived class (see @ref set_clone function). This default implementation is empty. */ void cloned(const X &src) { } /** @This is called when the reference counter has just been increased. The call is performed on reference to @tt X class so a virtual prototype must be declared in the @tt X class to override this function from further derived classes. This default implementation is empty. @see ref_decreased @see ref_inc */ void ref_increased(int ref_count) const { } /** Same as @ref ref_increased but called when the counter has been decreased. This default implementation is empty. @see ref_dec */ void ref_decreased(int ref_count) const { } /** @This increases references count on object. It should only be used on rare cases when smart pointer classes can not be used to track all references to the object. @see ref_drop */ void ref_inc() const { #ifdef _DPP_USE_GCC_ATOMIC # if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ __sync_add_and_fetch(&const_cast(this)->_raw, 1); # elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ __sync_add_and_fetch(&const_cast(this)->_raw, 2); # else # error __BYTE_ORDER__ not defined # endif #else const_cast(this)->_ref_count++; #endif static_cast(this)->ref_increased(_ref_count); } /** @This decreases references count on object. Dynamically allocated objects are deleted if counter reaches 0. It should only be used on rare cases when smart pointer classes can not be used to track all references to the object. @see ref_inc */ void ref_drop() const { assert(_ref_count > 0); #ifdef _DPP_USE_GCC_ATOMIC # if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ __sync_sub_and_fetch(&const_cast(this)->_raw, 1); # elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ __sync_sub_and_fetch(&const_cast(this)->_raw, 2); # endif #else const_cast(this)->_ref_count--; #endif static_cast(this)->ref_decreased(_ref_count); // free dynamically allocated objects only if (_dynamic && _ref_count == 0) delete this; } private: /** reference counter value */ union { struct { int _ref_count:31; int _dynamic:1; }; int _raw; }; }; } #endif