1 module vsignal.slot; 2 3 // https://issues.dlang.org/show_bug.cgi?id=5710 4 /** 5 Binds a function to a Slot. A payload can be passed if the function to call is 6 a data member function of the same or if the function accepts as its first 7 parameter a reference to its type. 8 9 Params: 10 pred = the function to bind. 11 slot = the Slot that holds the function. 12 instance = the payload to keep. 13 */ 14 package void connect(alias pred, F)(auto ref Slot!F slot) 15 { 16 import vsignal.utils : tryForward; 17 18 with(slot) 19 { 20 payload = null; 21 fn = (const scope void*, Parameters args) { 22 static if (is(ReturnType == void)) 23 pred(tryForward!(pred, args)); 24 else 25 return ReturnType(pred(tryForward!(pred, args))); 26 }; 27 } 28 } 29 30 @safe pure nothrow @nogc unittest 31 { 32 Slot!(void delegate(int)) slot; 33 34 class C 35 { 36 static void opCall(int) {} 37 static void fooA(const int) {} 38 static void fooB(ref int) {} 39 static void fooC(const ref int) {} 40 } 41 42 // all lambda listeners can omit the type 43 slot.connect!((ref _) {}); 44 slot.connect!((const ref _) {}); 45 slot.connect!((_) {}); 46 slot.connect!((const _) {}); 47 48 slot.connect!(C.opCall); 49 slot.connect!(C.fooA); 50 slot.connect!(C.fooB); 51 slot.connect!(C.fooC); 52 } 53 54 /// 55 package void connect(alias pred, T, F)(auto ref Slot!F slot, ref T instance) 56 { 57 with(slot) 58 { 59 payload = () @trusted { return cast(from!"std.traits".Unqual!T*) &instance; } (); 60 fn = (const scope void* payload, Parameters args) 61 { 62 import core.lifetime : forward; 63 import vsignal.utils : invoke; 64 65 T* type = () @trusted { return cast(T*) payload; } (); 66 67 static if (is(ReturnType == void)) 68 invoke!pred(*type, forward!args); 69 else 70 return ReturnType(invoke!pred(*type, forward!args)); 71 }; 72 } 73 } 74 75 @safe pure nothrow @nogc unittest 76 { 77 Slot!(void delegate(int)) slot; 78 79 struct Listener 80 { 81 void opCall(int) {} 82 void fooA(const int) {} 83 void fooB(ref int) {} 84 void fooC(const ref int) {} 85 } 86 87 Listener listener; 88 89 slot.connect!(Listener.opCall)(listener); 90 slot.connect!(Listener.fooA)(listener); 91 slot.connect!(Listener.fooB)(listener); 92 slot.connect!(Listener.fooC)(listener); 93 } 94 95 package struct Slot(F) 96 { 97 import vsignal.utils : from; 98 99 alias SlotType = F; 100 alias ReturnType = from!"std.traits".ReturnType!F; 101 alias Parameters = from!"std.traits".Parameters!F; 102 alias Function = from!"std.traits".SetFunctionAttributes!( 103 ReturnType delegate(const scope void*, Parameters), 104 from!"std.traits".functionLinkage!SlotType, 105 from!"std.traits".functionAttributes!SlotType 106 ); 107 108 auto opCall(Parameters args) 109 { 110 return fn(payload, from!"core.lifetime".forward!args); 111 } 112 113 bool opEquals(F)(const Slot!F other) const 114 { 115 return fn.funcptr is other.fn.funcptr && payload is other.payload; 116 } 117 118 void reset() 119 { 120 fn = null; 121 payload = null; 122 } 123 124 bool opCast(T : bool)() const 125 { 126 return fn.funcptr !is null; 127 } 128 129 package: 130 Function fn; 131 void* payload; 132 } 133 134 /// 135 unittest 136 { 137 @safe struct Listener 138 { 139 int i; 140 141 auto foo(int var) return 142 { 143 i = var; 144 return &this; 145 } 146 } 147 148 Listener listener; 149 alias myfoo = Listener.foo; 150 151 Slot!(Listener* delegate(int) @safe) slot; 152 153 slot.connect!(myfoo)(listener); 154 155 assert(slot(4) is &listener); 156 assert(listener.i == 4); 157 }