1 module vsignal.signal; 2 3 import vsignal.sink; 4 import vsignal.slot; 5 6 struct Signal(F) 7 if (is(F : RT delegate(Args), RT, Args...)) 8 { 9 /** 10 Publish a signal. 11 12 Params: 13 args = the Signal's function parameters to call each listener with. 14 */ 15 void emit(Slot!F.Parameters args) 16 { 17 import core.lifetime : forward; 18 19 foreach (call; calls) 20 cast(void) call(forward!args); 21 } 22 23 /** 24 Get a Sink to connect and disconnect listeners from a Signal. 25 26 This function can be @trusted if the lifetime of the Sink does not extend the 27 lifetime of the Signal. 28 29 Note: DIP1000 can detect if a reference escapes and as such it can infer @safe. 30 31 Returns: A newly constructed Sink. 32 */ 33 Sink!F sink(this This)() 34 { 35 // https://issues.dlang.org/show_bug.cgi?id=22309 36 auto sinkImpl = (ref return This signal) => Sink!F(&signal); 37 38 return sinkImpl(this); 39 } 40 41 bool empty() const @property 42 { 43 return !calls.length; 44 } 45 46 size_t length() const @property 47 { 48 return calls.length; 49 } 50 51 package: 52 Slot!F[] calls; 53 } 54 55 /// 56 unittest 57 { 58 Signal!(void delegate(int) @safe pure nothrow) signal; 59 Signal!(void delegate(ref int) @safe pure nothrow) signalRef; 60 61 static struct Listener 62 { 63 int i; 64 void opCall()(const ref int) 65 { 66 i++; 67 } 68 } 69 70 Listener listener; 71 72 signal.sink.connect!(Listener.opCall)(listener); 73 signalRef.sink.connect!(Listener.opCall)(listener); 74 75 int var; 76 77 signal.emit(var); 78 signalRef.emit(var); 79 80 assert(listener.i == 2); 81 } 82 83 /// 84 unittest 85 { 86 Signal!(void delegate(int)) signal; 87 int var = 35; 88 89 signal.sink.connect!((ref i, const int x) { 90 assert(&i is &var); 91 i += x; 92 })(var); 93 94 signal.emit(3); 95 96 assert(var == 38); 97 } 98 99 unittest 100 { 101 Signal!(void delegate(int)) sig; 102 103 version(vsignal_dip1000) 104 static assert( __traits(compiles, () @safe { sig.sink; } )); // calling Sink is @safe 105 else 106 static assert(!__traits(compiles, () @safe { sig.sink; } )); // calling Sink is @system 107 108 static assert(!__traits(compiles, () @safe => Signal!(void delegate(int)).sink)); 109 } 110 111 unittest 112 { 113 Signal!(void delegate(int)) sig; 114 115 struct Listener 116 { 117 int i; 118 void opCall(int var) { i = var; } 119 } 120 121 Listener listener; 122 123 Slot!(void delegate(int)) slotA; 124 Slot!(void delegate(int)) slotB; 125 126 slotA.connect!(Listener.opCall)(listener); 127 slotB.connect!((ref i) => listener.i++ ); 128 129 sig.calls = [ 130 slotA, 131 slotB 132 ]; 133 134 sig.emit(5); 135 assert(listener.i == 6); 136 }