SafeSlot.h

Go to the documentation of this file.
00001 /*
00002  * The information in this file is
00003  * Copyright(c) 2007 Ball Aerospace & Technologies Corporation
00004  * and is subject to the terms and conditions of the
00005  * GNU Lesser General Public License Version 2.1
00006  * The license text is available from   
00007  * http://www.gnu.org/licenses/lgpl.html
00008  */
00009 
00010 #ifndef SAFE_SLOT_H
00011 #define SAFE_SLOT_H
00012 
00013 #include "AppVerify.h"
00014 #include "Observer.h"
00015 #include "Slot.h"
00016 
00017 #include <memory>
00018 #include <string>
00019 #include <sstream>
00020 #include <typeinfo>
00021 
00022 /**
00023  *  \cond INTERNAL
00024  */
00025 namespace boost
00026 {
00027    class any;
00028 }
00029 /// \endcond
00030 class Subject;
00031 
00032 class SlotInvalidator;
00033 
00034 /**
00035  *  Subclass of Slot that allows the specification of an object whose destructor
00036  *  will cause the Slot to be automatically detached. This addresses the problem
00037  *  of stale pointers being left behind when an observer is destroyed while
00038  *  attached to a Subject. 
00039  *
00040  *  The SafeSlot constructor takes a third argument that is an 'invalidator.' 
00041  *  If this argument is \c NULL, the SafeSlot behaves exactly like a normal 
00042  *  Slot. If it is not \c NULL, the destructor of the invalidator object will 
00043  *  cause the SafeSlot to be invalidated. An invalid slot's update method will
00044  *  never be called, and it will be automatically removed from the Subject's
00045  *  list of slots.
00046  *
00047  *  A SafeSlot does not need to be detached when its invalidator goes away, 
00048  *  because the SafeSlot will be automatically destroyed.
00049  *
00050  *  @see    Slot
00051  */
00052 class SafeSlot : public Slot
00053 {
00054    friend class SubjectImpPrivate;
00055    friend class SlotInvalidator;
00056 
00057 public:
00058    /**
00059     *  Creates an empty slot object.
00060     *
00061     *  The default constructor creates an empty slot object. The object will
00062     *  return \c false from isValid() and will test equal to other empty slots.
00063     */
00064    SafeSlot() : Slot()
00065    {
00066    }
00067 
00068    /**
00069     *  Creates an auto-detaching slot.
00070     *
00071     *  Creates an auto-detaching slot. Assuming neither of the first two input 
00072     *  arguments is \c NULL, the %SafeSlot will return \c true from isValid() and will 
00073     *  test equal to other SafeSlots created with the same arguments.
00074     *
00075     *  @param   pT
00076     *           The object to call in Slot::update.
00077     *
00078     *  @param   pMethod
00079     *           The method to call on the object in Slot::update. The method 
00080     *           should not be virtual. Virtual slot methods may cause 
00081     *           Subject::detach to fail. If a virtual slot is needed, make the
00082     *           slot method non-virtual and have it call a virtual method. On
00083     *           notify, the slot method will be called as follows:
00084     *           @code
00085     *           (pT->*pMethod)(subject, signal, v);
00086     *           @endcode
00087     *           pMethod needs to have the following signature:
00088     *           @code
00089     *           T::method(Subject &subject, const std::string &signal, const boost::any &v);
00090     *           @endcode
00091     *
00092     *  @param   pInvalidator
00093     *           If this is non- \c NULL, when pInvalidator is destroyed,
00094     *           this slot will automatically detach from the Subject it is attached to.
00095     *           If this is \c NULL, the SafeSlot will behave like a normal slot.
00096     */
00097    template<class T, typename Method>
00098    SafeSlot(T *pT, Method pMethod, SlotInvalidator *pInvalidator) : Slot()
00099    {
00100       if(VERIFYNR(pT != NULL && pMethod != NULL))
00101       {
00102          mpSlot = new SafeSlotValue<T>(pT, pMethod, pInvalidator);
00103       }
00104    }
00105 
00106    /**
00107     *  Creates a copy of the input slot.
00108     *
00109     *  @param   slot
00110     *           The %Slot to copy from.
00111     */
00112    SafeSlot(const Slot& slot) :
00113       Slot(slot)
00114    {
00115    }
00116 
00117    /**
00118     *  Destroys the %SafeSlot, detaching from the Subject if necessary.
00119     */
00120    ~SafeSlot()
00121    {
00122       invalidate();
00123    }
00124 
00125 protected:
00126    /**
00127     * A class that extends the Slot::SlotWrapper class to add the ability to 
00128     * query an invalidator. Derived classes need to provide an implementation
00129     * of this method.
00130     */
00131    class SafeSlotWrapper
00132    {
00133    public:
00134       /**
00135        * Returns the invalidator object associated with this slot wrapper.
00136        *
00137        * @return  The invalidator the slot was created with.
00138        */
00139       virtual SlotInvalidator *getInvalidator() = 0;
00140    };
00141 
00142 private:
00143    void invalidate()
00144    {
00145       delete mpSlot;
00146       mpSlot = NULL;
00147    }
00148 
00149    SlotInvalidator *getInvalidator()
00150    {
00151       SafeSlotWrapper *pWrapper = dynamic_cast<SafeSlotWrapper*>(mpSlot);
00152       if (pWrapper != NULL)
00153       {
00154          return pWrapper->getInvalidator();
00155       }
00156       return NULL;
00157    }
00158 
00159    template<class T>
00160    class SafeSlotValue : public SlotValue<T>, public SafeSlotWrapper
00161    {
00162    public:
00163       SafeSlotValue(T *pT, void (T::*pMethod)(Subject&,const std::string&,const boost::any&), 
00164          SlotInvalidator *pInvalidator) : SlotValue<T>(pT, pMethod), mpInvalidator(pInvalidator)
00165       {
00166       }
00167 
00168       SafeSlotValue(const SlotValue<T> &base, 
00169          SlotInvalidator *pInvalidator) : SlotValue<T>(base), mpInvalidator(pInvalidator)
00170       {
00171       }
00172 
00173       SlotWrapper* clone() const
00174       {
00175          return new SafeSlotValue<T>(*this, mpInvalidator);
00176       }
00177 
00178       const std::type_info &getType() const
00179       {
00180          return typeid(SafeSlotValue<T>);
00181       }
00182 
00183       SlotInvalidator *getInvalidator()
00184       {
00185          return mpInvalidator;
00186       }
00187 
00188       bool matches(const SlotWrapper& rhs) const
00189       {
00190          // Two SafeSlotValues test equal if the target and method are the 
00191          // same and they both have or both don't have an invalidator. If
00192          // they both have invalidators, the exact value of the invalidator
00193          // doesn't matter.
00194          const SafeSlotValue<T> *pRhs = dynamic_cast<const SafeSlotValue<T> *>(&rhs);
00195          if (mpInvalidator)
00196          {
00197             return pRhs != NULL && pRhs->mpInvalidator != NULL && SlotValue<T>::matches(rhs);
00198          }
00199          else // this has no invalidator
00200          {
00201             // A SafeSlotValue tests equal to a SlotValue if the 
00202             // SafeSlotValue has a NULL invalidator and the other two 
00203             // members are equal. We can't just call SlotValue<T>::matches
00204             // because it will do the type check which will fail.
00205             if (pRhs != NULL && pRhs->mpInvalidator != NULL)
00206             {
00207                return false;
00208             }
00209             else // neither has an invalidator
00210             {
00211                std::auto_ptr<const SlotValue<T> > pThisCopy(dynamic_cast<const SlotValue<T>*>(this->SlotValue<T>::clone()));
00212                const SlotValue<T>& rhsValue = static_cast<const SlotValue<T>&>(rhs);
00213                std::auto_ptr<const SlotValue<T> > pRhsCopy(dynamic_cast<const SlotValue<T>*>(
00214                   rhsValue.SlotValue<T>::clone()));
00215                return pThisCopy->matches(*(pRhsCopy.get()));
00216             }
00217          }
00218       }
00219 
00220    private:
00221       SlotInvalidator *mpInvalidator;
00222    };
00223 };
00224 
00225 /**
00226  *  This class provides an object that, when destroyed invalidates a slot.  It
00227  *  is for use with the auto-NULLing SafeSlot class.
00228  *  
00229  *  Typically, an observer object that has a slot will inherit from or contain
00230  *  a SlotInvalidator. The invalidator will be provided to the SafeSlot 
00231  *  constructor and ensures that the Subject the slot is attached to does not
00232  *  hold a stale pointer if the observer object is destroyed while still attached.
00233  *
00234  *  @see    SafeSlot, AutoSlot
00235  */
00236 class SlotInvalidator
00237 {
00238    friend class SubjectImpPrivate;
00239 public:
00240    /**
00241     *  The SlotInvalidator constructor.
00242     */
00243    SlotInvalidator() : mpSlot(NULL) {}
00244 
00245    /**
00246     *  The SlotInvalidator destructor.
00247     */
00248    ~SlotInvalidator()
00249    {
00250       if (mpSlot)
00251       {
00252          mpSlot->invalidate();
00253       }
00254    }
00255 
00256 private:
00257    void activate(SafeSlot *pSlot)
00258    {
00259       mpSlot = pSlot;
00260    }
00261 
00262    void deactivate()
00263    {
00264       mpSlot = NULL;
00265    }
00266 
00267    SafeSlot *mpSlot;
00268 };
00269 
00270 /**
00271  *  Subclass of SafeSlot for convenience when the observer and invalidator
00272  *  are the same object.
00273  *
00274  *  @see    SafeSlot
00275  */
00276 class AutoSlot : public SafeSlot
00277 {
00278 public:
00279    /**
00280     *  Creates an auto-detaching slot.
00281     *
00282     *  Creates a slot. Assuming neither of the input arguments is \c NULL, the 
00283     *  slot will return \c true from isValid() and will test equal to other slots
00284     *  created with the same arguments. This slot will automatically detach
00285     *  from the subject it is attached to when the observer is destroyed.
00286     *
00287     *  @param   pObserver
00288     *           The object to call in Slot::update. It must inherit from 
00289     *           SlotInvalidator.
00290     *
00291     *  @param   pMethod
00292     *           The method to call on the object in Slot::update. The method 
00293     *           should not be virtual. Virtual slot methods may cause 
00294     *           Subject::detach to fail. If a virtual slot is needed, make the
00295     *           slot method non-virtual and have it call a virtual method. On
00296     *           notify, the slot method will be called as follows:
00297     *           @code
00298     *           (pT->*pMethod)(subject, signal, v);
00299     *           @endcode
00300     *           pMethod needs to have the following signature:
00301     *           @code
00302     *           T::method(Subject &subject, const std::string &signal, const boost::any &v);
00303     *           @endcode
00304     */
00305    template<class T, typename Method>
00306    AutoSlot (T *pObserver, Method pMethod) : SafeSlot(pObserver, pMethod, pObserver)
00307    {
00308    }
00309 };
00310 
00311 #endif

Software Development Kit - Opticks 4.9.0 Build 16218