/* CVSNT Generic API Copyright (C) 2004 Tony Hoyle and March-Hare Software Ltd This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. This library 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 library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* _EXPORT */ #ifndef EVS_SMARTPTR__H #define EVS_SMARTPTR__H #include <assert.h> #include "evs_malloc.h" namespace evs { template<typename _Typ> struct sp_delete { static void dealloc(_Typ* ptr) { delete ptr; } }; template<typename _Typ> struct sp_delete_array { static void dealloc(_Typ* ptr) { delete[] ptr; } }; template<typename _Typ> struct sp_free { static void dealloc(_Typ* ptr) { evs::free(ptr); } }; template<typename _Typ, typename _ArrayType = _Typ&, typename _Dealloc = sp_delete<_Typ> > class smartptr { public: typedef smartptr<_Typ,_ArrayType,_Dealloc> my_type; smartptr() { ppStub=NULL;} ~smartptr() { deref(); }; // De-consting is OK here because we're not modifying the object itself, only // reference counting smartptr(const my_type& other) { ((my_type&)other).myref(); ppStub=((my_type&)other).ppStub; } smartptr(_Typ* other) { alloc_ref(other); } template<typename _tIx> _ArrayType operator[](const _tIx _Ix) const { assert(ppStub); return _getstub()->obj->operator[](_Ix); } void ref() { myref(); } _Typ* detach() { assert(ppStub); if(!ppStub) return NULL; _Typ* ob = _getstub()->obj; if(_getstub()->object_count==1) { _getstub()->obj=NULL; } deref(); return ob; } my_type& operator=(const my_type& other) { if(_getstub()==other._getstub()) return *this; ((my_type&)other).myref(); deref(); ppStub=((my_type&)other).ppStub; return *this; } my_type& operator=(_Typ* other) { if(!other) { deref(); } else if(ppStub) { assert(_getstub()->obj!=other); deref(); alloc_ref(other); } else { alloc_ref(other); } return *this; } _Typ* operator->() const { assert(ppStub);if(!ppStub) return NULL; return _getstub()->obj; } operator _Typ*() const { if(!ppStub) return NULL; return _getstub()->obj; } _Typ* object() const { assert(ppStub); if(!ppStub) return NULL; return _getstub()->obj; } _Typ* copy() const { assert(ppStub); if(!ppStub) return NULL; return new _Typ(*(_getstub()->obj)); } static _Typ* create() { return new _Typ; } static _Typ* copy(const _Typ* obj) { return new _Typ(*obj); } void replace(my_type& other) { myreplace(other); } bool operator==(const my_type& other) const { if(!ppStub && !other.ppStub) return true; if(!ppStub || !other.ppStub) return false; return _getstub()->obj==other._getstub()->obj; } bool operator==(const _Typ* obj) const { if(!ppStub && !obj) return true; if(!ppStub) return false; return _getstub()->obj==obj; } template<typename _Ty> _Ty coerce(_Ty ob) { return dynamic_cast<_Ty>(object()); }; template<typename _Ty> _Ty coerce() { return dynamic_cast<_Ty>(object()); }; template<typename _Ty> bool iskindof(_Ty ob) { return coerce<_Ty>()?true:false; }; template<typename _Ty> bool iskindof() { return coerce<_Ty>()?true:false; }; private: template<typename _Ty> struct smartptr_stub { // object_count = global count of object references // instance_count = number of copies of this particular pointer // obj = object // pStub = indirect reference to real object // // Sum of all instance_count pointing to an object will == object_count smartptr_stub<_Ty>* pStub; size_t object_count,instance_count; _Ty obj; }; typedef smartptr_stub<_Typ*>* stub_ptr_type; stub_ptr_type ppStub; inline stub_ptr_type _getstub() const { return ppStub?ppStub->pStub:NULL; } void deref() { if(!ppStub) return; if(ppStub->instance_count) --ppStub->instance_count; if(ppStub->pStub->object_count && !--ppStub->pStub->object_count) dealloc_ref(); // FIXME: I'm getting instances of the original object instance count going to zero before // all the objects are at zero. Don't see how this can happen and it looks like a bug in the // code for it to be possible. Hack workaround to stop this crashing the software. else if(!ppStub->instance_count && ppStub!=ppStub->pStub) { // Object still exists, but all the copies in this group are done delete ppStub; } ppStub = NULL; } void myref() { if(!ppStub) return; ++ppStub->pStub->object_count; ++ppStub->instance_count; } // TODO: make these less time consuming - prealloc block of memory and use that void alloc_ref(_Typ* obj) { ppStub = new smartptr_stub<_Typ*>; ppStub->object_count = 1; ppStub->instance_count = 1; ppStub->obj = obj; ppStub->pStub = ppStub; } void dealloc_ref() { assert(ppStub); if(!ppStub) return; assert(!ppStub->pStub->object_count); if(ppStub->pStub->obj) _Dealloc::dealloc(ppStub->pStub->obj); if(ppStub->pStub!=ppStub) delete ppStub->pStub; delete ppStub; ppStub = NULL; } // Essentially this ppStub becomes a pointer to the real one, so we dispose of // our object and add our instance count to the object count of the new object void myreplace(my_type& other) { if(!ppStub) operator=(other); else if(!other.ppStub) deref(); else { ppStub->pStub = other.ppStub->pStub; ppStub->pStub->object_count+=ppStub->instance_count; ppStub->object_count=0; if(ppStub->obj) _Dealloc::dealloc(ppStub->obj); ppStub->obj = NULL; } } }; }; #endif