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