arbeit
Main Page | Namespace List | Class Hierarchy | Alphabetical List | Compound List | File List | Namespace Members | Compound Members | File Members

signal.h

Go to the documentation of this file.
00001 /*
00002 * $Id: signal.h,v 1.5 2003/09/22 09:28:34 jmk Exp $
00003 */
00004 ///////////////////////////////////////////////////////////////////////////
00005 //              _____________  ______________________    __   __  _____
00006 //             /  ________  |  |   ___   ________   /   |  \ /  \   |
00007 //            |  |       |  |_ |  |_ |  |       /  /    \__  |      |
00008 //            |  |  ___  |  || |  || |  |      /  /        | |      |
00009 //            |  | |   \ |  || |  || |  |     /  /      \__/ \__/ __|__
00010 //            |  | |_@  ||  || |  || |  |    /  /          Institute
00011 //            |  |___/  ||  ||_|  || |  |   /  /_____________________
00012 //             \_______/  \______/ | |__|  /___________________________
00013 //                        |  |__|  |
00014 //                         \______/
00015 //                    University of Utah       
00016 //                           2002
00017 //
00018 // By Joe Kniss, with help from Yarden Livnat
00019 ///////////////////////////////////////////////////////////////////////////
00020 
00021 //signal.h
00022 
00023 #ifndef __GUTZ_SIGNAL_DOT_H
00024 #define __GUTZ_SIGNAL_DOT_H
00025 
00026 #include <iostream>
00027 #include <vector>
00028 #include "signalCall.h"
00029 #include "signalIF.h"
00030 
00031 namespace gutz {
00032 
00033 // connect forward decl
00034 template<class SIG, class CE, class F>
00035 void connect( SIG &s,          //< the signal
00036               CE *callee,      //< pointer to class with slot
00037               F fncPtr,        //< member function of "callee" (the slot)
00038               int priority = 0 //< optional priority, higher = earlier called
00039              );
00040 
00041 // disconnect forward decl
00042 template<class SIG, class CE, class F>
00043 void disconnect( SIG &s,       //< the signal
00044                  CE *callee,   //< pointer to class with slot
00045                  F fncPtr      //< member function of "callee" (the slot)
00046                 );
00047 
00048 ////////////////////////////////////////////////////////////////////////////
00049 /// An API for signals and slots. 
00050 /// See also gutz::connect and gutz::disconnect in signalGutz.h
00051 ///
00052 /// If a class has member functions that will be used as slots, ie, 
00053 ///   functions that recieve signals from other classes, you need to 
00054 ///   have this public declaration in that class:
00055 ///
00056 ///  \code HAS_SLOTS; \endcode
00057 ///
00058 /// Other than that, a slot is simply any member function of a class, 
00059 ///  that's it!
00060 ///    here is an example class that... HAS_SLOTS;
00061 /// \code
00062 /// class MyClassB {
00063 ///    public: // blah blah constructors...
00064 ///       HAS_SLOTS;
00065 ///       void setTheFloat(float f) {...} /// a slot taking a float
00066 ///       void someEmptySlot();           /// a slot with 0 parameters...
00067 ///                                       /// ... defined elsewhere
00068 /// };
00069 /// \endcode
00070 ///  Notice that we only have "HAS_SLOTS;" declared once!  That's all you 
00071 ///   need no matter how many different slots you have.  Also notice that 
00072 ///   you don't need the "gutz::" namespace resolution, we took care of that 
00073 ///   for you.  HAS_SLOTS is a #define that declares a member variable in 
00074 ///   your class, it is used to keep track of any signal that connects to 
00075 ///   a slot.  This is needed so that you can delete the class whenever you 
00076 ///   need without having to worry about disconnecting every signal attached 
00077 ///   to it.  
00078 ///
00079 /// If you want to create a signal, i.e. a "function" that emmits an event,
00080 ///   you need to use the Signal<*> that is appropriate for the number of 
00081 ///   arguments that it will need, ex:
00082 /// \code
00083 /// class MyClassA {
00084 ///    public: //...
00085 ///       gutz::Signal<float>  myFloatChanged;
00086 /// }
00087 /// \endcode
00088 /// "myFloatChanged" can now be used like a function that takes 1 float
00089 ///    argument:
00090 /// \code
00091 ///  myFloatChanged(1.0f);
00092 /// \endcode
00093 ///
00094 ///  Remember that although you use myFloatChanged like a function, it is 
00095 ///    really a "functor", a class that has "operator()" overloaded so that
00096 ///    it behaves like a function.  For you this only affects the way it is
00097 ///    declared, as above, where the items in "<>"s are the types of the 
00098 ///    parameters used when it is called. \n
00099 ///  A signal that takes 0 parameters looks like:
00100 /// \code
00101 ///  gutz::Signal<>  myEmptySignal;
00102 /// \endcode
00103 ///
00104 ///  You can have up to 8 parameters in a signal right now, just declare your
00105 ///    signal with the number of parameters it will take, for example here is 
00106 ///    another signal that takes 4 parameters:
00107 /// \code
00108 ///  gutz::Signal<float, double, vec3f, const char *>  mySignalThingy;
00109 /// \endcode
00110 ///
00111 /// If you want a member function of a class to recieve a signal, you 
00112 ///    "connect" them together like so:
00113 /// \code
00114 /// gutz::connect(a.myFloatChanged, &b, &MyClassB::setTheFloat)
00115 /// \endcode
00116 /// This means {connect "a"'s signal "myFloatChanged" to "b"'s  member 
00117 ///    function "setTheFloat"}, ( where "a" is an instance of "MyClassA", 
00118 ///    and "b" is an instance of class "MyClassB").  Notice that "a" is 
00119 ///    passed to "connect" by reference, "b" is passed as a pointer, and 
00120 ///    "setTheFloat" is passed as a pointer to a member function. \n
00121 /// Remember that the class recieving the signal ("MyClassB" in this example) 
00122 ///    must have "HAS_SLOTS" inside it's declaration. 
00123 /// connect() also takes a priority integer value, the higher the number the
00124 ///    earlier the connected function will be called when there are 
00125 ///    multiple slots recieving the signal, mostly usefull for debugging,
00126 ///    probably not a usefull extension since we have no "real" symantics
00127 ///    associated with this value. See gutz::connect for more details.
00128 ///
00129 /// Signal Implementation Notes:\n 
00130 ///  an implementation of the Signal interface.
00131 ///  A generic signal that can take upto 8 parameters, the types are specified
00132 ///  by the template parameters A*, which default to the "not applicable" type,
00133 ///  which is simply an empty class.  The Call Type <signalCalls.h> is 
00134 ///  speciallized based on wether or not a parameter is a real type or NA to 
00135 ///  call the correct slot function. 
00136 ///
00137 /// TODO: could setup some kind of default parameters for the signal, with
00138 /// another huge list of template parameters, but is that really necessary?
00139 ///
00140 /// argument types, default to "not applicable" (NA) 
00141 ///////////////////////////////////////////////////////////////////////////
00142 template< class A1 = NA,  class A2 = NA,  class A3 = NA,  class A4 = NA,
00143           class A5 = NA,  class A6 = NA,  class A7 = NA,  class A8 = NA>
00144 class Signal : public SignalIF {
00145 public:
00146    typedef Signal<A1,A2,A3,A4,A5,A6,A7,A8>  MyType;
00147    typedef _CallIF<A1,A2,A3,A4,A5,A6,A7,A8> CallIFT;
00148    typedef std::vector<CallIFT*>            CallPVec;
00149    typedef typename CallPVec::iterator      CallPVecIter;
00150    Signal() {}
00151    ~Signal() 
00152    {
00153       /// notify all slots/calls that the signal is no longer valid
00154       for(int i=0; i<int(_calls.size()); ++i)
00155       {
00156          _calls[i]->detatch(this);
00157          delete _calls[i];
00158       }
00159    }
00160 
00161    ///////////////////////////////////////////////////////////////
00162    ///@name Functor call
00163    ///  Takes up to 8 args.
00164    ///  You only need to call this with the number of args
00165    ///  that are valid for the signal.
00166    ///@{
00167    void operator() (A1 a1=NA(), A2 a2=NA(), A3 a3=NA(), A4 a4=NA(), 
00168                     A5 a5=NA(), A6 a6=NA(), A7 a7=NA(), A8 a8=NA())
00169    {
00170       for(int i=0; i<int(_calls.size()); ++i)
00171       {
00172          dbg("operator()", i);
00173          _calls[i]->call(a1,a2,a3,a4,a5,a6,a7,a8);
00174       }
00175    }
00176    ///@}
00177    ///////////////////////////////////////////////////////////////
00178 
00179    ///////////////////////////////////////////////////////////////
00180    ///@name Utilities
00181    ///@{
00182    
00183    /// check if the slot has anyone attached to it.
00184    ///  You might want to know if calling the slot will have any 
00185    ///  affect.  This is especially important if the parameters
00186    ///  you are passing are not by reference and are "heavy", ie
00187    ///  large objects.
00188    bool slotsAttached() const { return _calls.size() != 0; }
00189    
00190    ///@}
00191    ///////////////////////////////////////////////////////////////
00192 
00193 
00194 protected:
00195 
00196    ///////////////////////////////////////////////////////////////
00197    ///@name Framework stuff.
00198    ///  You can ignore these functions, they are used by 
00199    ///   the signals and slots framework.  \n
00200    ///  Hiding these was easy using template friends, however,
00201    ///  sucky because we needed a full template declaration, even
00202    ///  though we would have liked a partial specialization of 
00203    ///  class where the signal is always us, ie MyType.
00204    ///@{
00205 
00206    template<class SIG, class CE, class F> 
00207       friend void connect(SIG&,CE*,F,int); 
00208 
00209    /// add a slot/call at users behest, used by gutz::connect
00210    template<class CE, class F>
00211    void addCall(CE *callee, F fncPtr, int priority)
00212    {
00213       dbg("addCall, priority", priority);
00214       dbg(typeid(CE).name());
00215       dbg(typeid(fncPtr).name());
00216       CallPVecIter i = _calls.begin();
00217       while( ( i != _calls.end() ) && ((*i)->_p > priority) ) ++i;
00218       _calls.insert(i, new Call<CE,F,A1,A2,A3,A4,A5,A6,A7,A8>(callee, 
00219                                                               fncPtr, 
00220                                                               priority, 
00221                                                               this));
00222    }
00223 
00224    template<class SIG, class CE, class F> 
00225       friend void disconnect(SIG&,CE*,F);
00226 
00227    /// remove slot/call at users behest. used by gutz::disconnect
00228    template<class CE, class F>
00229    void delCall(CE *callee, F fptr)
00230    {
00231       _Call<CE,F,A1,A2,A3,A4,A5,A6,A7,A8> tc(callee, fptr);
00232       CallPVecIter i = _calls.begin();
00233       while((i != _calls.end()) && ( _calls.size() ))
00234       {
00235          CallPVecIter tmpi = i + 1;
00236          if( (*i)->isCall(&tc) )
00237          {
00238             delete (*i);
00239             (*i) = 0;
00240             _calls.erase(i);
00241          }
00242          i = tmpi;
00243       }
00244    }
00245 
00246    friend class SignalTracker;
00247 
00248    /// remove slot/call when owner destructs...
00249    ///  from SignalIF interface, actually called by a
00250    ///  gutz::SignalTracker.
00251    void detatchSlotIF(void const *callee)
00252    {
00253       CallPVecIter i = _calls.begin();
00254       while((i != _calls.end()) && ( _calls.size() ))
00255       {
00256          CallPVecIter tmpi = i + 1;
00257          if( (*i)->isCallee(callee) )
00258          {
00259             delete (*i);
00260             (*i) = 0;
00261             _calls.erase(i);
00262          }
00263          i = tmpi;
00264       }
00265    }
00266 
00267    ///@}
00268    ///////////////////////////////////////////////////////////////
00269 
00270    /// printing debug statements
00271    inline void dbg(const char *where=0, int i1=-8888, int i2=-8888)
00272    {
00273       #if 0 /// switch debug off, even in debug mode
00274       #ifdef _DEBUG
00275         std::cerr << "Signal::";
00276         if(where)
00277            std::cerr << where; 
00278         if(i1 != -8888)
00279            std::cerr << " : " << i1;
00280         if(i2 != -8888)
00281            std::cerr << ", " << i2; 
00282         std::cerr << std::endl;
00283       #endif
00284       #endif
00285    }
00286 
00287    CallPVec  _calls;   
00288 };
00289 
00290 } //< end namespace gutz
00291 
00292 #endif
00293 
00294 

Send questions, comments, and bug reports to:
jmk