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 }