Slot.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 SLOT_H
00011 #define SLOT_H
00012 
00013 #include "AppVerify.h"
00014 #include "Observer.h"
00015 
00016 #include <string>
00017 #include <sstream>
00018 #include <typeinfo>
00019 
00020 /**
00021  *  \cond INTERNAL
00022  */
00023 namespace boost
00024 {
00025    class any;
00026 }
00027 /// \endcond
00028 class Subject;
00029 
00030 /**
00031  *  Class for specifying which method to call in response to a call to notify
00032  *  by a Subject.
00033  *
00034  *  Typically, only the constructors of this class will be used outside of the
00035  *  underlying implementation of the Subject class. 
00036  *
00037  *  When a Subject calls 'notify' with a particular signal name, all slots that
00038  *  are attached to that signal on the Subject will have their update method
00039  *  called. The slot's update method will in turn call the method on the 
00040  *  object provided via the slot's constructor. Slot methods need to have the
00041  *  following signature:
00042  *
00043  *  @code
00044  *  void T::method(Subject &subject, const std::string &signal, const boost::any &v);
00045  *  @endcode
00046  *
00047  *  When called, the arguments will be as follows:
00048  *  subject: the Subject notifying with the signal
00049  *  signal: the name of the signal the Subject notified
00050  *  v: data provided to the notify method by the Subject
00051  *
00052  *  When a %Slot is attached to a signal on a Subject, if the slot method is on
00053  *  an object derived from Observer, the object's attached method will be 
00054  *  called. Similarly, when a %Slot is detached from an object derived from
00055  *  Observer, the object's detached method will be called.
00056  *
00057  *  @see    Subject::attach, Subject::detach, Observer::attached, Observer::detached
00058  */
00059 class Slot
00060 {
00061    friend class SubjectImpPrivate;
00062    friend class SafeSlot;
00063 
00064 public:
00065    /**
00066     *  Creates an empty Slot object.
00067     *
00068     *  The default constructor creates an empty %Slot object. The object will
00069     *  return false from isValid and will test equal to other empty %Slots.
00070     */
00071    Slot() : mpSlot(NULL)
00072    {
00073    }
00074 
00075    /**
00076     *  Creates a slot.
00077     *
00078     *  Creates a slot. Assuming neither of the input arguments is NULL, the 
00079     *  %Slot will return true from isValid and will test equal to other %Slots
00080     *  created with the same arguments.
00081     *
00082     *  @param   pT
00083     *           The object to call in Slot::update.
00084     *
00085     *  @param   pMethod
00086     *           The method to call on the object in Slot::update. The method 
00087     *           should not be virtual. Virtual slot methods may cause 
00088     *           Subject::detach to fail. If a virtual slot is needed, make the
00089     *           slot method non-virtual and have it call a virtual method. On
00090     *           notify, the slot method will be called as follows:
00091     *           @code
00092     *           (pT->*pMethod)(subject, signal, v);
00093     *           @endcode
00094     *           pMethod needs to have the following signature:
00095     *           @code
00096     *           T::method(Subject &subject, const std::string &signal, const boost::any &v);
00097     *           @endcode
00098     */
00099    template<class T, typename Method>
00100    Slot(T *pT, Method pMethod) : mpSlot(NULL)
00101    {
00102       if(VERIFYNR(pT != NULL && pMethod != NULL))
00103       {
00104          mpSlot = new SlotValue<T>(pT, pMethod);
00105       }
00106    }
00107 
00108    /**
00109     *  Creates a copy of the input slot.
00110     *
00111     *  @param   slot
00112     *           The %Slot to copy from.
00113     */
00114    Slot(const Slot& slot) :
00115       mpSlot(NULL)
00116    {
00117       if (slot.isValid() == true)
00118       {
00119          mpSlot = slot.mpSlot->clone();
00120       }
00121    }
00122 
00123    /**
00124     *  Copies the state of one slot into another slot.
00125     *
00126     *  @param   rhs
00127     *           The %Slot to copy from: on the right-hand-side of the =.
00128     *
00129     *  @return   a reference to the slot assigned to.
00130     */
00131    Slot& operator=(const Slot& rhs)
00132    {
00133       SlotWrapper *pSlot = NULL;
00134       if (rhs.isValid())
00135       {
00136          pSlot = rhs.mpSlot->clone();
00137       }
00138       if (isValid())
00139       {
00140          delete mpSlot;
00141       }
00142       mpSlot = pSlot;
00143       return *this;
00144    }
00145 
00146    /**
00147     *  Indicates if the %Slot is a valid %Slot.
00148     *
00149     *  @return  false if the %Slot was created via the default constructor or
00150     *             copied from an invalid %Slot, or true otherwise.
00151     */
00152    bool isValid() const
00153    {
00154       return mpSlot != NULL;
00155    }
00156 
00157    /**
00158     *  Calls the method on the Slot.
00159     *
00160     *  @param   subject
00161     *            The Subject that called notify.
00162     *
00163     *  @param   signal
00164     *            The name of the signal the Subject is notifying.
00165     *
00166     *  @param   data
00167     *            The ancillary data the Subject provided to its notification.
00168     */
00169    void update(Subject &subject, const std::string &signal, const boost::any &data) const
00170    {
00171       if (isValid())
00172       {
00173          mpSlot->update(subject, signal, data);
00174       }
00175    }
00176 
00177    /**
00178     *  Calls the attached method on the observer, if it derives from Observer.
00179     *
00180     *  This method is typically only called by Subject::attach.
00181     *
00182     *  @param   subject
00183     *            The Subject that the slot is being attached to.
00184     *
00185     *  @param   signal
00186     *            The name of the signal the %Slot is being attached to.
00187     */
00188    void callAttachMethod(Subject &subject, const std::string &signal) const
00189    {
00190       if (isValid())
00191       {
00192          mpSlot->callAttachMethod(subject, signal, *this);
00193       }
00194    }
00195 
00196    /**
00197     *  Calls the detached method on the observer, if it derives from Observer.
00198     *
00199     *  This method is typically only called by Subject::detach.
00200     *
00201     *  @param   subject
00202     *            The Subject that the slot is being detached from.
00203     *
00204     *  @param   signal
00205     *            The name of the signal the %Slot is being detached from.
00206     */
00207    void callDetachMethod(Subject &subject, const std::string &signal) const
00208    {
00209       if (isValid())
00210       {
00211          mpSlot->callDetachMethod(subject, signal, *this);
00212       }
00213    }
00214 
00215    /**
00216     *  Indicates if two %Slots are equivalent.
00217     *
00218     *  @param   rhs
00219     *            The slot on the right-hand-side of the comparison.
00220     *
00221     *  @return  true if the two Slots are both invalid or if they were
00222     *            constructed with the same arguments, or false otherwise.
00223     */
00224    bool operator==(const Slot& rhs) const
00225    {
00226       if (!isValid())
00227       {
00228          return (!rhs.isValid());
00229       }
00230       else if (!rhs.isValid())
00231       {
00232          return false;
00233       }
00234       else // both are valid
00235       {
00236          return mpSlot->matches(*rhs.mpSlot);
00237       }
00238    }
00239 
00240    /**
00241     *  Gets the typeid of the object the %Slot was constructed with.
00242     *
00243     *  @return  the typeid of the object the %Slot was constructed with or
00244     *             typeid(void) if the %Slot is invalid.
00245     */
00246    const std::type_info &getType() const
00247    {
00248       if (isValid())
00249       {
00250          return mpSlot->getType();
00251       }
00252 
00253       return typeid(void);
00254    }
00255 
00256    /**
00257     *  Destroys the %Slot.
00258     */
00259    ~Slot()
00260    {
00261       if (mpSlot)
00262       {
00263          delete mpSlot;
00264          mpSlot = NULL;
00265       }
00266    }
00267 
00268 protected:
00269    /**
00270     *  This class provides an abstract interface that implements the details of
00271     *  the slot and hides the type information behind a generic interface.
00272     */
00273    class SlotWrapper
00274    {
00275    public:
00276       /**
00277        *  Calls the method on the Slot.
00278        *
00279        *  @param   subject
00280        *            The Subject that called notify.
00281        *
00282        *  @param   signal
00283        *            The name of the signal the Subject is notifying.
00284        *
00285        *  @param   data
00286        *            The ancillary data the Subject provided to its notification.
00287        */
00288       virtual void update(Subject &subject, const std::string &signal, const boost::any &data) const = 0;
00289 
00290       /**
00291        *  Creates a copy of the object.
00292        *
00293        *  @return  A copy of the object.
00294        */
00295       virtual SlotWrapper* clone() const = 0;
00296 
00297       /**
00298        *  Indicates if the supplied object matches this object.
00299        *
00300        *  @param rhs
00301        *             The object to compare with.
00302        *
00303        *  @return \c true if the two objects match and false otherwise.
00304        */
00305       virtual bool matches(const SlotWrapper& rhs) const = 0;
00306 
00307       /**
00308        *  Returns the type information of the concrete type of the object.
00309        *
00310        *  @return  The type information of the concrete object.
00311        */
00312       virtual const std::type_info &getType() const = 0;
00313 
00314       /**
00315        *  Calls the attached method on the observer, if it derives from Observer.
00316        *
00317        *  This method is typically only called by Subject::attach.
00318        *
00319        *  @param   subject
00320        *            The Subject that the slot is being attached to.
00321        *
00322        *  @param   signal
00323        *            The name of the signal the %Slot is being attached to.
00324        *
00325        *  @param   slot
00326        *            The slot that is being attached.
00327        */
00328       virtual void callAttachMethod(Subject &subject, const std::string &signal, const Slot &slot) = 0;
00329 
00330       /**
00331        *  Calls the detached method on the observer, if it derives from Observer.
00332        *
00333        *  This method is typically only called by Subject::detach.
00334        *
00335        *  @param   subject
00336        *            The Subject that the slot is being detached from.
00337        *
00338        *  @param   signal
00339        *            The name of the signal the %Slot is being detached from.
00340        *
00341        *  @param   slot
00342        *            The slot that is being detached.
00343        */
00344       virtual void callDetachMethod(Subject &subject, const std::string &signal, const Slot &slot) = 0;
00345    };
00346 
00347    SlotWrapper *mpSlot;
00348 
00349 private:
00350    template<class T>
00351    class SlotValue : public SlotWrapper
00352    {
00353    public:
00354       SlotValue(T *pT, void (T::*pMethod)(Subject&,const std::string&,const boost::any&)) : mpTarget(pT), mpMethod(pMethod)
00355       {
00356       }
00357 
00358       virtual ~SlotValue() {};
00359 
00360       virtual void update(Subject& subject, const std::string& signal, const boost::any& data) const
00361       {
00362          (mpTarget->*mpMethod)(subject, signal.c_str(), data);
00363       }
00364 
00365       virtual void callAttachMethod(Subject& subject, const std::string& signal, const Slot& slot)
00366       {
00367          Observer *pObs = dynamic_cast<Observer*>(mpTarget);
00368          if (pObs != NULL)
00369          {
00370             pObs->attached(subject, signal, slot);
00371          }
00372       }
00373 
00374       virtual void callDetachMethod(Subject& subject, const std::string& signal, const Slot& slot)
00375       {
00376          Observer *pObs = dynamic_cast<Observer*>(mpTarget);
00377          if (pObs != NULL)
00378          {
00379             pObs->detached(subject, signal, slot);
00380          }
00381       }
00382 
00383       virtual SlotWrapper* clone() const
00384       {
00385          return new SlotValue<T>(mpTarget, mpMethod);
00386       }
00387 
00388       virtual const std::type_info& getType() const
00389       {
00390          return typeid(SlotValue<T>);
00391       }
00392 
00393       virtual bool matches(const SlotWrapper& rhs) const
00394       {
00395          if (getType() == rhs.getType())
00396          {
00397             const SlotValue<T>& rhsValue = static_cast<const SlotValue<T>&>(rhs);
00398             return (mpTarget == rhsValue.mpTarget && mpMethod == rhsValue.mpMethod);
00399          }
00400          else
00401          {
00402             return false;
00403          }
00404       }
00405 
00406    private:
00407       T *mpTarget;
00408       void (T::*mpMethod)(Subject&,const std::string&,const boost::any&);
00409    };
00410 };
00411 
00412 /**
00413  *  Class for specifying a signal to re-emit.
00414  *
00415  *  Only the constructors of this class will be used outside of the
00416  *  underlying implementation if the Subject class. 
00417  *
00418  *  When a Subject calls 'notify' with a particular signal name, all Signals 
00419  *  that are attached to that signal on the Subject will have their update method
00420  *  called. The Signal's update method will in turn call notify on it's Subject
00421  *  using the Signal's signal name.
00422  *
00423  *  For example:
00424  *  @code
00425  *  pSubject->attach(SIGNAL_NAME(Subject, Modified), Signal(pObj, SIGNAL_NAME(Subject, Modified)));
00426  *  @endcode
00427  *  This causes pSubject->notify(SIGNAL_NAME(Subject, Modified, any)); to result in
00428  *  pObj->notify(SIGNAL_NAME(Subject, Modified), v) being called. Remember that NULL
00429  *  is a wildcard for signal names. Using NULL as the first argument to attach
00430  *  in the case above will cause all signals to be forwarded. Using NULL for 
00431  *  second argument to the Signal ctor will cause the name of the original
00432  *  signal to be re-used when the signal is re-emited. E.g.
00433  *  @code
00434  *  pSubject->attach(NULL, Signal(pObj, NULL));
00435  *  pSubject->notify("MySignal", any(myData));
00436  *  @endcode
00437  *  This causes pObj->notify("MySignal", any(myData)) to be called.
00438  *
00439  *  @see    Subject::attach, Subject::detach
00440  */
00441 class Signal : public Slot
00442 {
00443    friend class SubjectImp;
00444 
00445 public:
00446    /**
00447     *  Creates a %Signal.
00448     *
00449     *  @param   pSubject
00450     *           The Subject to re-emit the %Signal from.
00451     *
00452     *  @param   signalName
00453     *           The name to re-emit the signal as. If NULL, the signal will be
00454     *           re-emited with its original name.
00455     */
00456    Signal(Subject* pSubject, const std::string& signalName) : Slot()
00457    {
00458       if (pSubject != NULL)
00459       {
00460          mpSlot = new SignalValue(*pSubject, signalName);
00461       }
00462    }
00463 
00464 private:
00465    class SignalValue : public SlotWrapper
00466    {
00467    public:
00468       SignalValue(Subject& subject, const std::string& signalName) :
00469          mSubject(subject),
00470          mSignalName(signalName)
00471       {
00472       }
00473 
00474       virtual ~SignalValue() {};
00475 
00476       virtual void update(Subject& subject, const std::string& signal, const boost::any& data) const;
00477 
00478       virtual SlotWrapper *clone() const
00479       {
00480          return new SignalValue(mSubject, mSignalName);
00481       }
00482 
00483       virtual bool matches(const SlotWrapper& rhs) const
00484       {
00485          if (getType() == rhs.getType())
00486          {
00487             const SignalValue& rhsValue = static_cast<const SignalValue&>(rhs);
00488             return (&mSubject == &rhsValue.mSubject && mSignalName == rhsValue.mSignalName);
00489          }
00490          else
00491          {
00492             return false;
00493          }
00494       }
00495 
00496       virtual const std::type_info& getType() const
00497       {
00498          return typeid(SignalValue);
00499       }
00500 
00501       virtual void callAttachMethod(Subject& subject, const std::string& signal, const Slot& slot)
00502       {
00503       }
00504 
00505       virtual void callDetachMethod(Subject& subject, const std::string& signal, const Slot& slot)
00506       {
00507       }
00508 
00509    private:
00510       SignalValue& operator=(const SignalValue& rhs);
00511 
00512       Subject& mSubject;
00513       std::string mSignalName;
00514    };
00515 };
00516 
00517 #endif

Software Development Kit - Opticks 4.9.0 Build 16218