00001 /* 00002 This file is part of the Virtual Object System of 00003 the Interreality project (http://interreality.org). 00004 00005 Copyright (C) 2001-2003 Peter Amstutz 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Lesser General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Lesser General Public License for more details. 00016 00017 You should have received a copy of the GNU Lesser General Public 00018 License along with this library; if not, write to the Free Software 00019 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00020 00021 Peter Amstutz <tetron@interreality.org> 00022 */ 00023 #ifndef _REFCOUNT_HH_ 00024 #define _REFCOUNT_HH_ 00025 00026 /** @file 00027 Defines and inline implements RefCount. 00028 */ 00029 00030 #include <vos/corelibs/vos/vosdefs.hh> 00031 00032 #include <set> 00033 00034 /** @def pREF(type, ptr, value, body) 00035 @deprecated Use vRef 00036 @param type the pointer type (Foo*) of the referred-to object 00037 @param ptr the variable that will be used in this lexical block 00038 @param value the value assigned to the named variable, this should be is where refcounted object is acquired 00039 @param body relevant code that operates on the variable 00040 00041 Please note that pREF and rREF have been deprecated in favor of 00042 the vRef smart pointer template! However they still show up from 00043 time to time in the code... 00044 00045 This macro defines a lexical scope in which you have a reference 00046 to a reference-counted object. The purpose of this macro is to 00047 simplify tracking the acquire/release pair in the most common case 00048 of accessing a refcounted object by calling release() for you when 00049 you're clearly done accessing the refcounted object. 00050 This example should make it clearer: 00051 @code 00052 pREF(Sphere*, sphere, MetaObject::meta_cast<Sphere*>(MetaFactory::createLocalObject(&ls, typeid(Sphere).name(), 0));, 00053 sphere->setAccessControl(&NoPropertyAccessControl::static_NoPropertyAccessControl); 00054 sphere->setMaterial("#FF0000"); 00055 // more sphere->setSomething(); method calls 00056 model->insertChild(-1, "ball", sphere); 00057 ); 00058 @endcode 00059 This macro evaluates to the following (edited for readability): 00060 @code 00061 { 00062 Sphere* sphere = MetaObject::meta_cast<Sphere*>(MetaFactory::createLocalObject(&ls, typeid(Sphere).name(), 0)); 00063 try { 00064 sphere->setAccessControl(&NoPropertyAccessControl::static_NoPropertyAccessControl); 00065 sphere->setMaterial("#FF0000"); 00066 // more sphere->setSomething(); method calls 00067 model->insertChild(-1, "ball", sphere); 00068 if(sphere) sphere->release(); 00069 } catch(exception x) { 00070 if(sphere) sphere->release(); 00071 throw; 00072 } 00073 } 00074 @endcode 00075 @note In this example, a new sphere object is allocated, it's 00076 state is set, and then it is (implicitly) released. Because the 00077 object has been inserted into another Vobject ("model") it will 00078 not be deleted. If it had not been acquired by another section of 00079 the program, closing this block (calling release()) would cause 00080 "sphere" to be deleted. Also note that acquire() should be called 00081 by the function RETURNING the value, so if you receive a 00082 refcounted object you should assume that the count has already 00083 been incremented. Note that if you do get a NULL assigned to your 00084 variable, it will detect that and not try to release, so you can 00085 use these macros with methods that may return '0' on error. You 00086 are only responsible for calling release() when you are done if 00087 you are not using this macro. Exceptions which inherit from the 00088 base class 'exception' will be caught, the refcount released 00089 appropriately, and re-thrown. If you 'return', 'continue', 00090 'break', 'goto' or otherwise jump to another part of your program 00091 while in one of these macro blocks, release() WILL NOT get called. 00092 Finally, if you get an error that looks like "macro `pREF' used 00093 with too many (#) args", this means the compiler has probably 00094 gotten confused on a statement like this: 00095 @code 00096 int foo, bar, baz; 00097 @endcode 00098 If this is the case, to un-confuse the compiler you need to 00099 seperate out each variable declaration. 00100 @code 00101 int foo; 00102 int bar; 00103 int baz; 00104 @endcode 00105 */ 00106 00107 /** @def rREF(type, ptr, value, body) 00108 Like pREF, only the value is a C++ reference (Foo&) instead of a pointer (Foo*). 00109 */ 00110 00111 #define rREF(type, ptr, value, body) { type ptr = value; \ 00112 try { body; (ptr).release(); \ 00113 } catch(...) { (ptr).release(); throw; } } 00114 #define pREF(type, ptr, value, body) { type ptr = value; \ 00115 try { body; if(ptr) (ptr)->release(); \ 00116 } catch(...) { if(ptr) (ptr)->release(); throw; } } 00117 namespace VOS 00118 { 00119 class RefCounted; 00120 00121 /** @class ObjectExciseListener refcount.hh vos/corelibs/vos/refcount.hh 00122 * 00123 * Interface by which an application can be notified 00124 * that an object wants to be excised. 00125 */ 00126 class VOS_API ObjectExciseListener 00127 { 00128 public: 00129 /** This will be called by the listened-to object when its 00130 RefCounted::excise() method is called. The application should take any 00131 appropriate measures to remove references (to avoid stale 00132 pointers and/or memory leaks) and then call release(). 00133 */ 00134 virtual void notifyObjectExcise(RefCounted* object) = 0; 00135 }; 00136 00137 /** @class RefCounted refcount.hh vos/corelibs/vos/refcount.hh 00138 * 00139 * This is a simple base class for reference counting 00140 objects. It also provides a facility for tracking 00141 references to this object, so that they may be notified 00142 when the application wants this object to be deleted. 00143 You can use vRef to automatically release RefCounted objects 00144 when they would normally be destroyed (go out of scope). 00145 @note When you inherit from this make sure you inherit it as a 00146 @em virtual base class. That means 00147 @code 00148 class Foo : public virtual RefCounted { ... }; 00149 @endcode 00150 Otherwise you could have two (or more) seperate 00151 reference counters for the same object, and that won't do at all. 00152 */ 00153 class VOS_API RefCounted 00154 { 00155 private: 00156 int count; 00157 set<ObjectExciseListener*>* exciseListeners; 00158 public: 00159 /** Construct the refcount object with a starting count of 1 */ 00160 RefCounted() : count(1), exciseListeners(0) { }; 00161 00162 /** copy constructor, need to reset count for copy */ 00163 RefCounted(const RefCounted& /*rc*/) : count(1), exciseListeners(0) { }; 00164 00165 /** Destructor. (Cleans up excise listeners) */ 00166 virtual ~RefCounted() { if(exciseListeners) delete exciseListeners; } 00167 00168 /** This method is used by release() to destroy this object if the refcount 00169 * reaches 0. By default, it simply calls "delete this". 00170 * However, you can override it in a subclass if you need to do something 00171 * other than deleting this object. */ 00172 virtual void destroy(); 00173 00174 /** Increment the reference count. */ 00175 virtual void acquire(); 00176 00177 /** Decrement the reference count. The object will be deleted if (count == 0) */ 00178 virtual void release(); 00179 00180 /** Get the current reference count */ 00181 virtual int getCount(); 00182 00183 /** Add an excise listener. When the excise method is called on 00184 this object, each excise listener will be notified so that it 00185 may clean up any references to this object. 00186 */ 00187 virtual void addExciseListener(ObjectExciseListener* oel); 00188 00189 /** Remove an excise listener. 00190 @see addExciseListener() 00191 @param oel the ObjectExciseListener to remove 00192 @param releaseIfRefcounted Should we test the object excise 00193 listener to see if it is refcounted, and if so release it? 00194 Under certain special circumstances (such as calling 00195 "removeExciseListener(this)" from a destructor) this behavior 00196 is undesirable. 00197 */ 00198 virtual void removeExciseListener(ObjectExciseListener* oel, bool releaseIfRefcounted = true); 00199 00200 /** Try to cause all known references to this object to release 00201 their references so the object will be deleted, by calling 00202 ObjectExciseListener::notifyObjectExcise(). 00203 @note This method is virtual and objects making use of this 00204 class will probably want to supply their own code to detach 00205 from linking data structures. Overriding excise() will 00206 probably be sufficient for most uses; the purpose of the 00207 ObjectExciseListener facility is for plugin/application level hooks 00208 which may be beyond the scope of your immediate code. 00209 */ 00210 virtual void excise(); 00211 }; 00212 00213 /** @class vRef refcounted.hh vos/corelibs/vos/refcounted.hh 00214 * 00215 * This is a "smart pointer" wrapper class around any RefCounted object. vRef 00216 * automatically releases its contained object when it goes out of scope 00217 * (i.e., when it is destroyed). 00218 * Almost every function in VOS that returns a RefCounted object (e.g. 00219 * any MetaObject or Vobject) increments that object's reference count 00220 * before doing so. This means that you must use this class to store that 00221 * object, or manually call RefCounted::release() on the object when you 00222 * are done using it. 00223 * 00224 * Note that this class takes care of releasing your object automatically, 00225 * not acquiring it. For example, if you assign (with =) a pointer (or another vRef) 00226 * from some vRef and then store that pointer (or other vRef) beyond the life of 00227 * the original vRef object, its reference count will be incorrect: call acquire() 00228 * on the object when you store the pointer (or other vRef). 00229 * 00230 * However, the copy constructor <emphasis>does</emphasis> acquire the RefCounted object. 00231 * 00232 * For more discussion and examples, see the VOS manual and the tutorial 00233 * programs (in "apps/tutorials"). 00234 */ 00235 template<class T> class vRef 00236 { 00237 private: 00238 T* ptr; 00239 public: 00240 vRef() : ptr(0) { } 00241 vRef(T* x) : ptr(x) { } 00242 vRef(T& x) : ptr(&x) { } 00243 vRef(const vRef<T>& x) : ptr(x.ptr) { if(ptr) ptr->acquire(); } 00244 ~vRef() { if(ptr) ptr->release(); } 00245 00246 T* operator=(T* x) { if(ptr) ptr->release(); ptr = x; return x; } 00247 T& operator=(T& x) { if(ptr) ptr->release(); ptr = &x; return x; } 00248 00249 vRef<T>& operator=(const vRef<T>& x) { 00250 if(ptr) ptr->release(); 00251 ptr = x.ptr; 00252 if(ptr) ptr->acquire(); 00253 return *this; 00254 } 00255 00256 bool operator==(const vRef<T>& x) { return (x.ptr == ptr); } 00257 bool operator!=(const vRef<T>& x) { return (x.ptr != ptr); } 00258 00259 T& operator*() { return *ptr; } 00260 T* operator->() { return ptr; } 00261 T* operator&() { return ptr; } 00262 00263 void acquire() { ptr->acquire(); } 00264 void release() { ptr->release(); } 00265 }; 00266 } 00267 00268 #endif