00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107 contentsChanged.emit(myVal);
00108
00109 }
00110 \endcode
00111
00112
00113 \subsection blockingsignals Blocking Signals
00114
00115 An object that is a SignalManager can temporarily be made to turn off its ordinary signals by means of the signalsBlocked(bool) property.
00116 \code
00117 Widget w = new Widget;
00118
00119 w.signalsBlocked = true;
00120 contentsChanged.emit(myVal);
00121 \endcode
00122
00123 For the rare occasion that a signal must ignore the block of its owner, it can be defined as a NonBlockingSignal. Such a signal can be used to emit a <tt>destroyed</tt> signal in the destructor, for example.
00124 \code
00125 class Widget : SignalManager
00126 {
00127 mixin SignalManagement;
00128 NonBlockingSignal!() destroyed;
00129 this()
00130 {
00131 destroyed = new NonBlockingSignal!(this);
00132 }
00133 ~this()
00134 {
00135 destroyed.emit();
00136 deleteSignals();
00137 }
00138 }
00139 \endcode
00140
00141 \note When you provide a destructor in a managing class, you are replacing the one that SignalManagement provides. You must therefore duplicate the contents from that destructor (with \c deleteSignals(); something like <tt>SM.~this()</tt>, where \c SM identifies the mixin, does not seem to work yet.).
00142
00143 \warning One should be careful to rely on signals in a destructor to be emitted. Garbage collected objects have no deterministic finalisation, and therefore the signal (\c destroyed in this case) may have been deleted before the owner gets deleted (\c Widget in this case). It will work as intended if you delete the owner explicitly however.
00144
00145
00146 \subsection freesignals Free Signals
00147
00148 A signal does not need to be owned, for example if it lives outside a class.
00149 \code
00150 Signal!() freeSignal = new Signal!()(null);
00151 \endcode
00152
00153 A free signal can not be blocked through its owner of course. Blocking signals individually is not supported yet.
00154
00155
00156
00157 \section slots Slots
00158
00159 Slots are managed as well.
00160 \code
00161 class Widget : SlotManager
00162 {
00163 mixin SlotManagement;
00164 \endcode
00165 \anchor careful_free_slots The importance of a managing owner is more apparent for slots than for signals. If a slot outlives its owner (due to the non-deterministic finalisation of garbage collected objects) they still receive signals and it has been shown that the member functions with which they are associated <i>may still be operational</i> despite the object having been destructed. SlotManagement takes care that connections are broken and that all relevant actors leave the scene at the right moment.
00166
00167 A slot is defined in the same way as signals are:
00168 \code
00169 Slot!(int) set;
00170 \endcode
00171 The slot should be related to one of the member functions at the time of initialisation:
00172 \code
00173 this()
00174 {
00175 set = new Slot!(int)(this, &setVal);
00176 }
00177 \endcode
00178 where \c setVal is the member function that you want to be called when a signal arrives,
00179 \code
00180 void setVal(int i) { my_int = i; }
00181 private:
00182 int my_int;
00183 }
00184 \endcode
00185
00186 The function reference on which a slot is created may be to any of the following
00187 <UL>
00188 <LI>a member function</LI>
00189 <LI>a static nested function</LI>
00190 <LI>a free function.</LI>
00191 </UL>
00192 \warning It is important that the slot object does not outlive the function that it references. Although you will be able to construct a slot that references a non-static nested function, it is not safe to do so. A segmentation fault will be the result if a slot attempts to call a function that has been garbage collected. For the same reason, when constructing a slot on a member function, that slot must be owned by the object (implementing a SlotManager) that has the member function. The destructor of the owner will remove the slot before the member function becomes garbage.
00193
00194 When the nested function goes out of scope, it may be collected as garbage before the slot is destructed, which will result in a segmentation fault if the slot receives a signal at this moment.
00195
00196 \subsection private Removing Duplicate Functionality in the API
00197
00198 In the above example, the class interface has two methods for accomplishing the same thing. Calling <tt>setVal(5)</tt> is equivalent to calling <tt>set(5)</tt>. To simplify the API, \c setVal() may be made private.
00199
00200 \subsection freeslots Free Slots (Experimental)
00201
00202 Analogous to free signals, a slot can live outside a class.
00203 \code
00204 void message()
00205 {
00206 printf("You can turn a free function into a slot as well.\n");
00207 }
00208
00209 void main()
00210 {
00211 Slot!() freeSlot = new Slot!()(null,&message);
00212 Signal!() freeSig = new Signal!()(null);
00213 connect(freeSig, freeSlot);
00214 freeSig.emit();
00215 }
00216 \endcode
00217 As mentioned \ref careful_free_slots "above", there is reason to be carefull with free slots, because their destruction is not linked with the life span of the function that they reference. It is best to disconnect free slots before their referenced function goes out of scope.
00218
00219 There is one scenario in which a segmentation fault will happen even when only static functions are referenced. Suppose a special signal has been implemented (inheriting from some Signal template) that is guaranteed to emit when its owner (or itself) is being destructed. When such a signal remains alive until the program terminates, it will emit during the final garbage collection. Connected slots that are still alive will receive the signal, and call their referenced functions. But, even static functions are being collected at this point, and may have been collected before the signals and slots. As a result, the program may terminate with a segmentation fault.
00220
00221 In the future, free slots may automatically register with a global static SlotManager. Before termination of the program, this manager may be called to disconnect all "free" slots, and thus prevent the segmentation fault.
00222
00223
00224 \section bothplease Both Signals and Slots
00225
00226 Classes that own both signals and slots should inherit from \c SignalSlotManager and mix in the \c SignalSlotManagement implementation.
00227
00228 Currently, dcouple supports signals and slots with up to seven arguments. More can easily be added, although their practicality can be questioned.
00229
00230
00231 \section interfacesmixins Extending Legacy Classes
00232
00233 \note Currently there is a <A HREF="http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D.bugs/1846">bug</A> in dmd involving interfaces. The following will be possible in the future.
00234
00235 Courtecy the concepts of interfaces and mixins of the D programming language, this implementation has the interesting property that existing (probably third-party) classes can be equipped with signals and slots without interfering with their inheritance tree. Given the following legacy class
00236 \code
00237 class ImportantClass : DeepInheritanceHierarchy
00238 {
00239
00240 P importantProperty() { return _prop; }
00241 void importantProperty(P prop) { _prop = prop; }
00242 private:
00243 P _prop;
00244 }
00245 \endcode
00246 A class like this can be extended in a straight forward manner, for example
00247 \code
00248 class ExtendedClass : ImportantClass, SignalSlotManager
00249 {
00250 mixin SignalSlotManagement;
00251 Signal!() propertyChanged;
00252 Slot!(P) updateProperty;
00253 this()
00254 {
00255 propertyChanged = new Signal!()(this);
00256 updateProperty = new Slot!(P)(this,&importantProperty);
00257 }
00258 void importantProperty(P prop)
00259 {
00260 super.importantProperty(prop);
00261 propertyChanged.emit();
00262 }
00263 }
00264 \endcode
00265
00266 Without (working) interfaces, SignalSlotManager must be a class, and therefore positioned at the root of the inheritance tree.
00267
00268
00269 \section examples Examples
00270
00271 More examples can be found in the <A HREF="../examples/">examples</A> directory.
00272
00273
00274 \section status Project Status
00275
00276 This framework is in early development. One of the aspects that have not been considered is thread-safety. Feedback is appreciated! Any volunteers to benchmark against <A HREF="../../Resources.txt">other implementations</A>?
00277
00278
00279 \section changelog Changelog
00280
00281 <UL>
00282 <LI> <b>version 0.3</b>
00283 <UL>
00284 <LI> Made to compile under dmd 0.131.
00285 <LI> Regenerated documentation with Doxygen 1.4.3.
00286 <LI> The management now works by means of an interface and mixin, so legacy classes can be equipped with signals and slots. Early dmd did not allow this due to a compiler bug.
00287 <LI> Multiple identical connections can exist. A slot receives a signal as many times per emit as connections were made.
00288 <LI> Slots receive a signal in the order in which they were connected.
00289 </UL>
00290 </UL>
00291
00292 */