interreality.org [VOS]
[Home] [About]
[Screenshots]
[Download]
[News]
[Community]
[Documentation] [Manual]
[Bugs & Requests] [Wiki]

/home/tetron/hack/vos/libs/vos/vutil/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 <http://www.interreality.org>
00022 */
00023 #ifndef _VUTIL_REFCOUNT_HH_
00024 #define _VUTIL_REFCOUNT_HH_
00025 
00026 /** @file
00027     Defines and inline implements RefCount.
00028 */
00029 
00030 #include <set>
00031 
00032 #include <vos/vutil/vutildefs.hh>
00033 #include <boost/thread/mutex.hpp>
00034 #include <boost/thread/recursive_mutex.hpp>
00035 
00036 namespace VUtil
00037 {
00038 #ifndef SWIG
00039 class RefCounted;
00040 #endif
00041 
00042 /** @class ObjectExciseListener refcount.hh vos/vos/refcount.hh
00043     @ingroup libvutil
00044 
00045     Interface by which an application can be notified
00046     that an object wants to be excised.
00047 */
00048 class VUTIL_API ObjectExciseListener
00049 {
00050 public:
00051     virtual ~ObjectExciseListener();
00052 
00053     /** This will be called by the listened-to object when its
00054         RefCounted::excise() method is called.  The application should take any
00055         appropriate measures to remove references (to avoid stale
00056         pointers and/or memory leaks) and then call release().
00057     */
00058     virtual void notifyObjectExcise(RefCounted* object) = 0;
00059 };
00060 
00061 /** @class RefCounted refcount.hh vos/vos/refcount.hh
00062     @ingroup libvutil
00063 
00064     This is a simple base class for reference counting
00065     objects.  It also provides a facility for tracking
00066     references to this object, so that they may be notified
00067     when the application wants this object to be deleted.
00068     You can use vRef to automatically release RefCounted objects
00069     when they would normally be destroyed (go out of scope).
00070 
00071     @note If you have a situation where you are doing multiple
00072     inheritance from two or more classes that (directly or indirectly)
00073     themselves inherit from RefCounted, you need to mark your
00074     inheritance as a virtual base class.  This means in your code
00075     @code
00076     class Foo : public virtual RefCounted { ... };
00077     class Bar : public virtual RefCounted { ... };
00078     class Baz : public virtual Foo, public virtual Bar { ... };
00079     @endcode
00080     Otherwise you could have two (or more) seperate
00081     reference counters for the same object, and that won't do at all.
00082 */
00083 #if !defined(SWIG)
00084 class VUTIL_API RefCounted
00085 {
00086 private:
00087     int count;
00088     boost::mutex count_mutex;
00089 
00090     std::set<ObjectExciseListener*>* exciseListeners;
00091     boost::recursive_mutex exciseListeners_mutex;
00092 public:
00093     /** Construct the refcount object with a starting count of 1 */
00094     RefCounted() : count(1), exciseListeners(0) { };
00095 
00096     /** copy constructor, need to reset count on the copy */
00097     RefCounted(const RefCounted& /*rc*/) : count(1), exciseListeners(0) { };
00098 
00099     /** Destructor. (Cleans up excise listeners) */
00100     virtual ~RefCounted() { if(exciseListeners) delete exciseListeners; }
00101 
00102     /** This method is used by release() to destroy this object if the
00103         refcount reaches 0.  By default, it simply calls "delete
00104         this".  However, you can override it in a subclass if you
00105         really want to do something other than deleting this object.
00106     */
00107     virtual void destroy();
00108 
00109     /** Increment the reference count. */
00110     virtual void acquire();
00111 
00112     /** Decrement the reference count.  The object will be deleted if (count == 0) */
00113     virtual void release();
00114 
00115     /** Get the current reference count */
00116     virtual int getRefCount();
00117 
00118     /** Add an excise listener.  When the excise() method is called on
00119         this object, each excise listener will be notified so that it
00120         may clean up any references to this object.
00121 
00122         @param oel the excise listener to call back.  This can itself
00123         be a refcounted object, and if so, its refcount will be incremented.
00124     */
00125     virtual void addExciseListener(ObjectExciseListener* oel);
00126 
00127     /** Remove an excise listener.
00128         @see addExciseListener()
00129         @param oel the ObjectExciseListener to remove
00130         @param releaseIfRefcounted Should we test the object excise
00131         listener to see if it is refcounted, and if so release it?
00132         Under certain special circumstances (such as calling
00133         "removeExciseListener(this)" from a destructor) this behavior
00134         is undesirable.
00135     */
00136     virtual void removeExciseListener(ObjectExciseListener* oel,
00137                                       bool releaseIfRefcounted = true);
00138 
00139     /** Ask that all known references to this object to release
00140         their references so the object can be deleted, by calling
00141         ObjectExciseListener::notifyObjectExcise().
00142 
00143         @note This method is virtual and objects making use of this
00144         class will probably want to supply their own additional code
00145         to detach from linking data structures.  Make sure to call
00146         RefCounted::excise() in yoru override method so that the
00147         excise listeners are called back.  Overriding excise() will
00148         probably be sufficient for most uses; the purpose of the
00149         ObjectExciseListener facility is for plugin/application level
00150         hooks which may be beyond the scope of your immediate code.
00151      */
00152     virtual void excise();
00153 };
00154 #endif
00155 
00156 /** @class NullPointerError refcount.hh vos/vos/refcount.hh
00157     @ingroup libvutil
00158 
00159     This is raised if you try and dereference a vRef which contains a
00160     null pointer.
00161  */
00162 class VUTIL_API NullPointerError : public std::runtime_error {
00163 public:
00164     NullPointerError(const std::string& s) : runtime_error(s) { };
00165 };
00166 
00167 
00168 /** @class vRef refcount.hh vos/vos/refcount.hh
00169     @ingroup libvutil
00170 
00171    This is a "smart pointer" wrapper class around any RefCounted
00172    object. When you assign a pointer to a vRef, it calls aquire(),
00173    increasing the refcount on that object.  When vRef goes out of
00174    scope (i.e., when it is destroyed) it automatically calls release
00175    on the wrapped object.  For more discussion and examples, see the
00176    VOS manual and the tutorial programs (in "apps/tutorials").
00177 
00178    @note vRef is not threadsafe. You must not share the same vRef
00179    instance between threads -- copy it instead!
00180  */
00181 
00182 template<class T> class vRef
00183 {
00184 private:
00185     T* ptr;
00186 
00187 public:
00188     /** Initialize an empty vRef that points to nothing. */
00189     vRef() : ptr(0) { }
00190 
00191     /** @param x Pointer to object to wrap in this vRef: it must be a RefCounted
00192             class or subclass.
00193         @param doacquire If true, object will be acquired and reference count
00194             will be incremented. If false, the object will retain it's current
00195             reference count.  Note that when a RefCounted object is created with
00196             "new" its reference count is already 1, and in this case you shouldn't
00197             increment the count.
00198     */
00199     vRef(T* x, bool doacquire) : ptr(x) { if(ptr && doacquire) ptr->acquire(); }
00200 
00201     /** @param x Reference to object to wrap in this vRef: it must be a
00202             RefCounted class or subclass.
00203         @param doacquire If true, object will be acquired and reference count
00204             will be incremented. If false, the object will retain it's current
00205             reference count.  Note that when a RefCounted object is created with
00206             "new" its reference count is already 1, and in this case you shouldn't
00207             increment the count.
00208 
00209     */
00210     vRef(T& x, bool doacquire) : ptr(&x) { if(ptr && doacquire) ptr->acquire(); }
00211 
00212     /** Create a new vRef containing the same object as another vRef. The
00213             reference count of the object will be incremented.
00214         @param x vRef to copy.
00215     */
00216     vRef(const vRef<T>& x) : ptr(x.ptr) { if(ptr) ptr->acquire(); }
00217 
00218     /** Create a new vRef containing the same object as another vRef. The
00219             reference count of the object will be incremented. This form
00220             allows the other vRef to use a different class in its template.
00221         @param x vRef to copy.
00222     */
00223     template<class T2> vRef(const vRef<T2>& x) {
00224         if(x.isValid()) {
00225             ptr = x.get();
00226             ptr->acquire();
00227         } else ptr = 0;
00228     }
00229 
00230     /** Destructor.  Decrements the reference count on the underlying
00231         pointer. */
00232     ~vRef() { if(ptr) ptr->release(); }
00233 
00234     /** Test whether this vRef holds a valid pointer or not.
00235         @return true if internal pointer is not null (0), false if
00236         internal pointer is null (0).
00237     */
00238     bool isValid() const { return (ptr != 0); }
00239 
00240 #ifndef SWIG
00241     /** @return true if the wrapped pointer is null, false if it is
00242         valid */
00243     bool operator!() const { return (ptr == 0); }
00244 
00245     bool operator==(const vRef& x) const { return (x.ptr == ptr); }
00246     bool operator!=(const vRef& x) const { return (x.ptr != ptr); }
00247 
00248     bool operator==(const T* x) const { return (x == ptr); }
00249     bool operator!=(const T* x) const { return (x != ptr); }
00250 
00251     bool operator==(const T& x) const { return ( (ptr != 0) && (x == *ptr) ); }
00252     bool operator!=(const T& x) const { return ( (ptr != 0) && (x != *ptr) ); }
00253 
00254     /** Assign a new object to this vRef. If this vRef is holding an object,
00255         that object will be released (it's reference count decremented).
00256         @param x the new pointer
00257         @param doacquire If true, object will be acquired and
00258         reference count will be incremented. If false, the object will
00259         retain it's current reference count.  When a new RefCounted
00260         object is created (e.g. with "new"), its reference count is
00261         already 1, and you don't need to increment the count.
00262     */
00263     template<class T2> vRef<T>& assign(T2* x, bool doacquire) {
00264         if(ptr) ptr->release();
00265         ptr = x;
00266         if(ptr && doacquire) ptr->acquire();
00267         return *this;
00268     }
00269 
00270     /** Assign to another vRef. Old wrapped object is released, and new
00271         wrapped object is acquired. */
00272     vRef<T>& operator=(const vRef<T>& x) {
00273         if(ptr) ptr->release();
00274         ptr = x.ptr;
00275         if(ptr) ptr->acquire();
00276         return *this;
00277     }
00278 
00279     /** Assign to another vRef. Old wrapped object is released, and new
00280         wrapped object is acquired. */
00281     template<class T2> vRef<T>& operator=(const vRef<T2>& x) {
00282         if(ptr) ptr->release();
00283         if(x.isValid()) {
00284             ptr = x.get();
00285             if(ptr) ptr->acquire();
00286         }
00287         return *this;
00288     }
00289 
00290     /** Return reference to wrapped object.
00291         @throw NullPointerError if internal pointer is 0 (no object held)
00292     */
00293     T& operator*() const {
00294         if(ptr) return *ptr;
00295         else throw NullPointerError("tried to dereference null smart pointer in * operator");
00296     }
00297 #endif
00298 
00299     /** Return pointer to wrapped object.
00300         @throw NullPointerError if internal pointer is 0 (no object held)
00301     */
00302     T* operator->() const {
00303         if(ptr) return ptr;
00304         else throw NullPointerError("tried to dereference null smart pointer in -> operator");
00305     }
00306 
00307     /** Return pointer to wrapped object
00308         @throw NullPointerError if internal pointer is 0 (no object held)
00309     */
00310     T* get() const {
00311         if(ptr) return ptr;
00312         else throw NullPointerError("tried to dereference null smart pointer in & operator");
00313     }
00314 
00315     /** Release object and set internal pointer to 0. */
00316     void clear() {
00317         if(ptr) ptr->release();
00318         ptr = 0;
00319     }
00320 
00321 
00322 #ifndef SWIG
00323 
00324     /** Cast to pointer.  Same as get().
00325             Usually used by implicit cast in a function call.
00326         @throw NullPointerError if internal pointer is 0 (no object held)
00327     */
00328     operator T*() {
00329         if(ptr) return ptr;
00330         else throw NullPointerError("tried to dereference null smart pointer in cast");
00331     }
00332 
00333     /** Cast to reference. Same as the operator*().
00334             Usually used by implicit cast in a function call.
00335         @throw NullPointerError if internal pointer is 0 (no object is held)
00336     */
00337     operator T&() {
00338         if(ptr) return *ptr;
00339         else throw NullPointerError("tried to dereference null smart pointer in cast to reference");
00340     }
00341 
00342 #endif // ifndef SWIG
00343 
00344 };
00345 
00346 }
00347 
00348 #endif