Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Compound List | File List | Namespace Members | Compound Members | File Members | Related Pages | Examples

vos/corelibs/vos/refcount.hh

Go to the documentation of this file.
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

Generated on Tue Aug 12 03:55:41 2003 for Interreality Project - VOS by doxygen 1.3.2