1 module des.arch.base;
2 
3 import std..string;
4 
5 import des.ts;
6 import des.log;
7 
8 import des.arch.util;
9 import des.arch.sig;
10 import des.arch.slot;
11 import des.arch.emm;
12 
13 ///
14 template isObject(alias f) { enum isObject = __traits(compiles,typeof(f)); }
15 
16 ///
17 template isSignalObj(alias f)
18 {
19     static if( isObject!f ) enum isSignalObj = isSignal!(typeof(f));
20     else enum isSignalObj = false;
21 }
22 
23 ///
24 interface DesBase : ExternalMemoryManager, SlotHandler
25 {
26     protected
27     {
28         ///
29         void createSlotController();
30 
31         void __createSignals();
32 
33         ///
34         final void prepareDES()
35         {
36             createSlotController();
37             __createSignals();
38         }
39     }
40 
41     ///
42     mixin template DES()
43     {
44         import des.arch.util;
45 
46         static if( !is(typeof(__DES_BASE_CLASS)) )
47         {
48             mixin EMM;
49 
50             enum __DES_BASE_CLASS = true;
51 
52             private SlotController __slot_controller;
53             final SlotController slotController() @property { return __slot_controller; }
54 
55             protected
56             {
57                 void createSlotController() { __slot_controller = newEMM!SlotController; }
58                 void __createSignals() { mixin( __createSignalsMixin!(typeof(this)) ); }
59             }
60         }
61         else
62         {
63             override protected
64             {
65                 void __createSignals() { mixin( __createSignalsMixin!(typeof(this)) ); }
66             }
67         }
68     }
69 
70     template allSignalNames(T)
71     {
72         template isSignalMember(string n)
73         {
74             static if( __traits(compiles,typeof(__traits(getMember,T,n))) )
75                 enum isSignalMember = isSignal!(typeof(__traits(getMember,T,n)));
76             else enum isSignalMember = false;
77         }
78 
79         alias allSignalNames = staticFilter!( isSignalMember, __traits(allMembers,T) );
80     }
81 
82     static final protected @property
83     {
84         string __createSignalsMixin(T)()//if( is( T : DesBase ) )
85         {
86             string[] ret;
87 
88             enum list = [ allSignalNames!T ];
89 
90             static if( list.length == 0 ) return "";
91             else
92             {
93                 foreach( sig; list )
94                     ret ~= format( "%1$s = newEMM!(typeof(%1$s))();", sig );
95 
96                 return ret.join("\n");
97             }
98         }
99     }
100 
101     ///
102     final auto newSlot(Args...)( void delegate(Args) fnc )
103     { return newEMM!(Slot!Args)( this, fnc ); }
104 
105     ///
106     final auto connect(Args...)( Signal!Args sig, void delegate(Args) fnc )
107     in { assert( sig !is null, "signal is null" ); } body
108     {
109         auto ret = newSlot!Args( fnc );
110         sig.connect( ret );
111         logger.Debug( "sig: %s, fnc: %s", sig, fnc );
112         return ret;
113     }
114 }
115 
116 ///
117 class DesObject : DesBase
118 {
119     mixin DES;
120     this() { prepareDES(); }
121 }
122 
123 ///
124 unittest
125 {
126     static class Sigsig : DesObject
127     {
128         mixin DES;
129         Signal!string message;
130         Signal!float number;
131 
132         Signal!(string,int) comp;
133     }
134 
135     static class C1 : DesObject
136     {
137         mixin DES;
138         string[] messages;
139         void notSlot( float x ) { }
140         void listen( string msg ) { messages ~= msg; }
141     }
142 
143     class C2 : C1
144     {
145         mixin DES;
146         float a;
147         void abcFunc12( float x ) { a = x + 3.15; }
148     }
149 
150     class C3 : DesObject
151     {
152         mixin DES;
153 
154         string str;
155         int num;
156 
157         void cfunc( string s, int i )
158         {
159             str = s;
160             num = i;
161         }
162     }
163 
164     auto sigsig = new Sigsig;
165     auto client = new C2;
166     auto c3 = new C3;
167 
168     sigsig.message.connect( client.newSlot( &(client.listen) ) );
169     auto client_abcFunc12_slot = sigsig.number.connect( client.newSlot( &(client.abcFunc12) ) );
170 
171     sigsig.message( "hello" );
172 
173     assertEq( client.messages.length, 1 );
174     assertEq( client.messages[0], "hello" );
175 
176     sigsig.number( 0 );
177 
178     assertEq( client.a, 3.15 );
179 
180     sigsig.number.disconnect( client_abcFunc12_slot );
181 
182     sigsig.number( 2 );
183     assertEq( client.a, 3.15 );
184     sigsig.number.connect( client_abcFunc12_slot );
185     sigsig.number( 2 );
186     assertEq( client.a, 5.15 );
187 
188     sigsig.comp.connect( c3.newSlot( &c3.cfunc ) );
189     sigsig.comp( "okda", 13 );
190     assertEq( c3.str, "okda" );
191     assertEq( c3.num, 13 );
192 }
193 
194 unittest
195 {
196     class Sig : DesObject
197     {
198         mixin DES;
199         Signal!(string,int) s;
200     }
201 
202     class Obj : DesObject {}
203 
204     auto sig = new Sig;
205     auto obj = new Obj;
206 
207     string[] str_arr;
208     int[] int_arr;
209 
210     sig.s.connect( obj.newSlot(( string s, int i )
211                 {
212                     str_arr ~= s;
213                     int_arr ~= i;
214                 }) );
215 
216     sig.s( "hello", 3 );
217     sig.s( "world", 5 );
218 
219     assertEq( str_arr, ["hello","world"] );
220     assertEq( int_arr, [3,5] );
221 }
222 
223 unittest
224 {
225     class C1 : DesObject
226     {
227         mixin DES;
228         Signal!(string) sig;
229     }
230 
231     class C2 : C1
232     {
233         string[] buf;
234         this() { connect( sig, (string s){ buf ~= s; } ); }
235     }
236 
237     auto c2 = new C2;
238 
239     c2.sig( "hello" );
240     assertEq( c2.buf, ["hello"] );
241 }
242 
243 
244 unittest
245 {
246     class C1 : DesObject
247     {
248         mixin DES;
249         Signal!() empty;
250         SignalBox!int number;
251     }
252 
253     auto c1 = new C1;
254 
255     bool empty_call = false;
256     c1.connect( c1.empty, { empty_call = true; } );
257 
258     assert( !empty_call );
259     c1.empty();
260     assert( empty_call );
261 
262     int xx = 0;
263     int ss = 0;
264     int ee = 0;
265     c1.connect( c1.number.begin, (int v){ ss = v; } );
266     c1.connect( c1.number.end, (int v){ ee = v; } );
267     c1.connect( c1.number, (int v){ xx = v; } );
268     assertEq( xx, 0 );
269     c1.number(12);
270     assertEq( xx, 12 );
271     assertEq( ss, 12 );
272     assertEq( ee, 12 );
273 }