/* 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 #include "evs_malloc.h" namespace evs { template struct sp_delete { static void dealloc(_Typ* ptr) { delete ptr; } }; template struct sp_delete_array { static void dealloc(_Typ* ptr) { delete[] ptr; } }; template struct sp_free { static void dealloc(_Typ* ptr) { evs::free(ptr); } }; template > 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 _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 _Ty coerce(_Ty ob) { return dynamic_cast<_Ty>(object()); }; template _Ty coerce() { return dynamic_cast<_Ty>(object()); }; template bool iskindof(_Ty ob) { return coerce<_Ty>()?true:false; }; template bool iskindof() { return coerce<_Ty>()?true:false; }; private: template 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