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