Mercurial > projects > qtd
comparison qt/qtd/MOC.d @ 288:f9559a957be9 signals
new signals and slots implementation
author | eldar |
---|---|
date | Sun, 08 Nov 2009 19:28:01 +0000 |
parents | |
children | 19498f420252 |
comparison
equal
deleted
inserted
replaced
287:b6984b290e46 | 288:f9559a957be9 |
---|---|
1 module qt.qtd.MOC; | |
2 | |
3 import qt.qtd.ctfe.Format; | |
4 | |
5 import std.typetuple; | |
6 | |
7 import qt.Signal; | |
8 import qt.qtd.MetaMarshall; | |
9 | |
10 public import std.traits; | |
11 /** | |
12 Utils. | |
13 */ | |
14 | |
15 T qMin(T)(T a,T b) { if (a < b) return a; return b; } | |
16 T qMax(T)(T a, T b) { if (a < b) return b; return a; } | |
17 T qBound(T)(T min, T val,T max) { return qMax(min, qMin(max, val)); } | |
18 | |
19 bool is_digit_char(const char s) | |
20 { | |
21 return (s >= '0' && s <= '9'); | |
22 } | |
23 | |
24 bool is_octal_char(const char s) | |
25 { | |
26 return (s >= '0' && s <= '7'); | |
27 } | |
28 | |
29 bool is_hex_char(const char s) | |
30 { | |
31 return ((s >= 'a' && s <= 'f') | |
32 || (s >= 'A' && s <= 'F') | |
33 || (s >= '0' && s <= '9') | |
34 ); | |
35 } | |
36 | |
37 int lastIndexOf(T)(T[] haystack, T[] needle, int from = -1) | |
38 { | |
39 auto l = haystack.length; | |
40 auto ol = needle.length; | |
41 int delta = l - ol; | |
42 if (from < 0) | |
43 from = delta; | |
44 if (from < 0 || from > l) | |
45 return -1; | |
46 if (from > delta) | |
47 from = delta; | |
48 | |
49 while(from >= 0) | |
50 { | |
51 if (haystack[from..from+ol] == needle) | |
52 return from; | |
53 from--; | |
54 } | |
55 return -1; | |
56 } | |
57 | |
58 | |
59 T[] newArray(T)(size_t len, T[] from = []) | |
60 { | |
61 if (len == from.length) | |
62 return from; | |
63 | |
64 if (!from.length) | |
65 from = [T.init]; | |
66 | |
67 if (from.length < len) | |
68 return newArray!T(len, from ~ from); | |
69 | |
70 return from[0..len]; | |
71 } | |
72 | |
73 string replicate(int n, char value) | |
74 { | |
75 char[] ret = "".dup; | |
76 if (n > 0) | |
77 { | |
78 // ret = newArray!char(n); | |
79 for(int i = 0; i < n; i++) | |
80 ret ~= value; | |
81 } | |
82 return cast(string)ret; | |
83 } | |
84 | |
85 template Repeat(T, int I) | |
86 { | |
87 static if (!I) alias TypeTuple!() Repeat; | |
88 else alias TypeTuple!(T, Repeat!(T, I - 1)) Repeat; | |
89 } | |
90 | |
91 /** | |
92 CTFE MOC port. | |
93 */ | |
94 | |
95 enum MethodFlags { | |
96 AccessPrivate = 0x00, | |
97 AccessProtected = 0x01, | |
98 AccessPublic = 0x02, | |
99 MethodMethod = 0x00, | |
100 MethodSignal = 0x04, | |
101 MethodSlot = 0x08, | |
102 MethodConstructor = 0x0c, | |
103 MethodCompatibility = 0x10, | |
104 MethodCloned = 0x20, | |
105 MethodScriptable = 0x40 | |
106 } | |
107 | |
108 enum Access { Private, Protected, Public } | |
109 | |
110 struct FunctionDef | |
111 { | |
112 /* FunctionDef(): returnTypeIsVolatile(false), access(Private), isConst(false), isVirtual(false), | |
113 inlineCode(false), wasCloned(false), isCompat(false), isInvokable(false), | |
114 isScriptable(false), isSlot(false), isSignal(false), | |
115 isConstructor(false), isDestructor(false), isAbstract(false) {} | |
116 */ | |
117 // Type type; | |
118 // string normalizedType; | |
119 // string tag; | |
120 // string name; | |
121 string sig; | |
122 string arguments; | |
123 Access access; | |
124 /* bool returnTypeIsVolatile; | |
125 | |
126 QList<ArgumentDef> arguments; | |
127 | |
128 enum Access { Private, Protected, Public }; | |
129 bool isConst; | |
130 bool isVirtual; | |
131 bool inlineCode; | |
132 bool wasCloned; | |
133 | |
134 QByteArray inPrivateClass; | |
135 bool isCompat; | |
136 bool isInvokable; | |
137 bool isScriptable; | |
138 bool isSlot; | |
139 bool isSignal; | |
140 bool isConstructor; | |
141 bool isDestructor; | |
142 bool isAbstract; | |
143 */ | |
144 } | |
145 | |
146 FunctionDef newSlot(string sig, string args) | |
147 { | |
148 return FunctionDef(sig, args, Access.Public); | |
149 } | |
150 | |
151 FunctionDef newSignal(string sig, string args) | |
152 { | |
153 return FunctionDef(sig, args, Access.Protected); | |
154 } | |
155 | |
156 struct Generator | |
157 { | |
158 string output; | |
159 string[] strings; | |
160 // QByteArray purestSuperClass; | |
161 // QList<QByteArray> metaTypes; | |
162 } | |
163 | |
164 int lengthOfEscapeSequence(string s, uint i) | |
165 { | |
166 if (s[i] != '\\' || i >= s.length - 1) | |
167 return 1; | |
168 const int startPos = i; | |
169 ++i; | |
170 auto ch = s[i]; | |
171 if (ch == 'x') { | |
172 ++i; | |
173 while (i < s.length && is_hex_char(s[i])) | |
174 ++i; | |
175 } else if (is_octal_char(ch)) { | |
176 while (i < startPos + 4 | |
177 && i < s.length | |
178 && is_octal_char(s[i])) { | |
179 ++i; | |
180 } | |
181 } else { // single character escape sequence | |
182 i = qMin(i + 1, s.length); | |
183 } | |
184 return i - startPos; | |
185 } | |
186 | |
187 int strreg(ref Generator gen, string s) | |
188 { | |
189 int idx = 0; | |
190 foreach (str; gen.strings) { | |
191 if (str == s) | |
192 return idx; | |
193 idx += str.length + 1; | |
194 foreach (i, c; str) { | |
195 if (c == '\\') { | |
196 int cnt = lengthOfEscapeSequence(str, i) - 1; | |
197 idx -= cnt; | |
198 i += cnt; | |
199 } | |
200 } | |
201 } | |
202 gen.strings ~= s; | |
203 return idx; | |
204 } | |
205 | |
206 void generateFunctions(ref Generator gen, FunctionDef[] list, string functype, byte type) | |
207 { | |
208 if (!list.length) | |
209 return; | |
210 gen.output ~= format_ctfe("\n // ${}s: signature, parameters, type, tag, flags\n", functype); | |
211 | |
212 foreach (i, f; list) { | |
213 byte flags = type; | |
214 | |
215 if (f.access == Access.Private) | |
216 flags |= MethodFlags.AccessPrivate; | |
217 else if (f.access == Access.Public) | |
218 flags |= MethodFlags.AccessPublic; | |
219 else if (f.access == Access.Protected) | |
220 flags |= MethodFlags.AccessProtected; | |
221 | |
222 gen.output ~= format_ctfe(" ${}, ${}, ${}, ${}, 0x${:x},\n", strreg(gen, f.sig), | |
223 strreg(gen, f.arguments), strreg(gen, ""/*f.normalizedType*/), strreg(gen, ""/*f.tag*/), flags); | |
224 } | |
225 } | |
226 | |
227 string generateCode(string className, FunctionDef[] signalList, FunctionDef[] slotList) | |
228 { | |
229 auto gen = Generator("", []); | |
230 | |
231 /* bool isQt = (cdef->classname == "Qt"); | |
232 bool isQObject = (cdef->classname == "QObject"); | |
233 bool isConstructible = !cdef->constructorList.isEmpty(); | |
234 | |
235 // | |
236 // build the data array | |
237 // | |
238 int i = 0; | |
239 | |
240 | |
241 // filter out undeclared enumerators and sets | |
242 { | |
243 QList<EnumDef> enumList; | |
244 for (i = 0; i < cdef->enumList.count(); ++i) { | |
245 EnumDef def = cdef->enumList.at(i); | |
246 if (cdef->enumDeclarations.contains(def.name)) { | |
247 enumList += def; | |
248 } | |
249 QByteArray alias = cdef->flagAliases.value(def.name); | |
250 if (cdef->enumDeclarations.contains(alias)) { | |
251 def.name = alias; | |
252 enumList += def; | |
253 } | |
254 } | |
255 cdef->enumList = enumList; | |
256 } | |
257 | |
258 | |
259 QByteArray qualifiedClassNameIdentifier = cdef->qualified; | |
260 qualifiedClassNameIdentifier.replace(':', '_'); | |
261 */ | |
262 bool isConstructible = false; | |
263 | |
264 FunctionDef[] propertyList, enumList, constructorList; | |
265 int index = 12; | |
266 gen.output ~= format_ctfe("static const uint[] qt_meta_data_${} = [\n", className); | |
267 gen.output ~= format_ctfe("\n // content:\n"); | |
268 gen.output ~= format_ctfe(" ${}, // revision\n", 2); | |
269 gen.output ~= format_ctfe(" ${}, // classname\n", strreg(gen, className)); | |
270 gen.output ~= format_ctfe(" ${}, ${}, // classinfo\n", 0, 0); | |
271 // index += cdef->classInfoList.count() * 2; | |
272 | |
273 int methodCount = signalList.length + slotList.length;// + cdef->methodList.count(); | |
274 gen.output ~= format_ctfe(" ${}, ${}, // methods\n", methodCount, methodCount ? index : 0); | |
275 index += methodCount * 5; | |
276 gen.output ~= format_ctfe(" ${}, ${}, // properties\n", propertyList.length, propertyList.length ? index : 0); | |
277 index += propertyList.length * 3; | |
278 // if(cdef->notifyableProperties) | |
279 // index += cdef->propertyList.count(); | |
280 gen.output ~= format_ctfe(" ${}, ${}, // enums/sets\n", enumList.length, enumList.length ? index : 0); | |
281 | |
282 // int enumsIndex = index; | |
283 // for (i = 0; i < cdef->enumList.count(); ++i) | |
284 // index += 4 + (cdef->enumList.at(i).values.count() * 2); | |
285 gen.output ~= format_ctfe(" ${}, ${}, // constructors\n", isConstructible ? constructorList.length : 0, | |
286 isConstructible ? index : 0); | |
287 | |
288 // | |
289 // Build classinfo array | |
290 // | |
291 // generateClassInfos(); | |
292 | |
293 // | |
294 // Build signals array first, otherwise the signal indices would be wrong | |
295 // | |
296 generateFunctions(gen, signalList, "signal", MethodFlags.MethodSignal); | |
297 | |
298 // | |
299 // Build slots array | |
300 // | |
301 generateFunctions(gen, slotList, "slot", MethodFlags.MethodSlot); | |
302 | |
303 // | |
304 // Build method array | |
305 // | |
306 // generateFunctions(cdef->methodList, "method", MethodMethod); | |
307 | |
308 | |
309 // | |
310 // Build property array | |
311 // | |
312 // generateProperties(); | |
313 | |
314 // | |
315 // Build enums array | |
316 // | |
317 // generateEnums(enumsIndex); | |
318 | |
319 // | |
320 // Build constructors array | |
321 // | |
322 // if (isConstructible) | |
323 // generateFunctions(cdef->constructorList, "constructor", MethodConstructor); | |
324 | |
325 // | |
326 // Terminate data array | |
327 // | |
328 gen.output ~= format_ctfe("\n 0 // eod\n];\n\n"); | |
329 | |
330 // | |
331 // Build stringdata array | |
332 // | |
333 gen.output ~= format_ctfe("static const string qt_meta_stringdata_${} = \n", className); | |
334 gen.output ~= format_ctfe(" \""); | |
335 int col = 0; | |
336 int len = 0; | |
337 foreach (i, s; gen.strings) { | |
338 len = s.length; | |
339 if (col && col + len >= 72) { | |
340 gen.output ~= format_ctfe("\"\n \""); | |
341 col = 0; | |
342 } else if (len && s[0] >= '0' && s[0] <= '9') { | |
343 gen.output ~= format_ctfe("\"\""); | |
344 len += 2; | |
345 } | |
346 int idx = 0; | |
347 while (idx < s.length) { | |
348 if (idx > 0) { | |
349 col = 0; | |
350 gen.output ~= format_ctfe("\"\n \""); | |
351 } | |
352 int spanLen = qMin(cast(uint)70, s.length - idx); | |
353 // don't cut escape sequences at the end of a line | |
354 int backSlashPos = s.lastIndexOf("\\", idx + spanLen - 1); | |
355 if (backSlashPos >= idx) { | |
356 int escapeLen = lengthOfEscapeSequence(s, backSlashPos); | |
357 spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, cast(int)(s.length - idx)); | |
358 } | |
359 gen.output ~= s[idx..idx+spanLen]; | |
360 idx += spanLen; | |
361 col += spanLen; | |
362 } | |
363 | |
364 gen.output ~= "\\0"; | |
365 col += len + 2; | |
366 } | |
367 gen.output ~= "\";\n\n"; | |
368 | |
369 return gen.output; | |
370 } | |
371 | |
372 string metaCallArgs(Args...)() | |
373 { | |
374 string res; | |
375 foreach(i, _; Args) { | |
376 if (i > 0) | |
377 res ~= ","; | |
378 res ~= metaCallArgument!(Args[i])("_a[" ~ __toString(i+1) ~ "]"); | |
379 } | |
380 return res; | |
381 } | |
382 | |
383 string qtDeclArgs(Args...)() | |
384 { | |
385 string ret; | |
386 foreach(i, _; Args) | |
387 { | |
388 if(i > 0) | |
389 ret ~= ","; | |
390 ret ~= qtDeclArg!(Args[i]); | |
391 } | |
392 return ret; | |
393 } | |
394 | |
395 string generate_qt_metacall(alias Signals, alias Slots)() | |
396 { | |
397 string res = " | |
398 protected int qt_metacall(QMetaObject.Call _c, int _id, void **_a) | |
399 { | |
400 _id = super.qt_metacall(_c, _id, _a); | |
401 if (_id < 0) | |
402 return _id;\n"; | |
403 | |
404 alias TypeTuple!(Signals.at, Slots.at) Methods; | |
405 enum methodCount = Methods.length; | |
406 if(methodCount) | |
407 { | |
408 res ~= " | |
409 if (_c == QMetaObject.Call.InvokeMetaMethod) { | |
410 switch (_id) {"; | |
411 foreach(i, bogus; Repeat!(void, methodCount)) { | |
412 res ~= " | |
413 case " ~ __toString(i) ~ ": " ~ MetaEntryName!(Methods[i].at) ~ "(" ~ metaCallArgs!(MetaEntryArgs!(Methods[i].at))() ~ "); break;"; | |
414 } | |
415 res ~= "\n default: ;\n }\n"; | |
416 res ~= " _id -= " ~ __toString(methodCount) ~ ";"; | |
417 res ~= "\n }"; | |
418 } | |
419 | |
420 res ~= "\n return _id; | |
421 }"; | |
422 return res; | |
423 } | |
424 | |
425 string dDeclArgs(Args...)() | |
426 { | |
427 string ret; | |
428 foreach(i, _; Args) | |
429 ret ~= ", " ~ Args[i].stringof; | |
430 return ret; | |
431 } | |
432 string genMetaMethodsConstr(alias Funcs)(string className) | |
433 { | |
434 string res; | |
435 enum funcsCount = Funcs.at.length; | |
436 foreach(i, bogus; Repeat!(void, funcsCount)) | |
437 { | |
438 res ~= " index++;\n" ~ | |
439 " _staticMetaObject.addMethod(new " ~ className ~ "(signature!(\"" ~ MetaEntryName!(Funcs.at[i].at) ~ "\"" ~ dDeclArgs!(MetaEntryArgs!(Funcs.at[i].at))()~ "), index));\n\n"; | |
440 } | |
441 return res; | |
442 } | |
443 string generateMetaObjectConstruction(alias Signals, alias Slots)() | |
444 { | |
445 string res; | |
446 res ~= "\n | |
447 private static void _populateMetaInfo() { | |
448 alias BaseClassesTuple!(typeof(this))[0] BaseClass; | |
449 int index = BaseClass.staticMetaObject().methodCount() - 1;\n\n"; | |
450 | |
451 res ~= genMetaMethodsConstr!(Signals)("QMetaSignal"); | |
452 res ~= genMetaMethodsConstr!(Slots)("QMetaSlot"); | |
453 | |
454 res ~= " | |
455 }\n"; | |
456 return res; | |
457 } | |
458 | |
459 string generateQMetaObject(string className) | |
460 { | |
461 string res; | |
462 res ~= " | |
463 QMetaObject metaObject() { return staticMetaObject; } | |
464 private static QMetaObject _staticMetaObject; | |
465 private static QMetaObjectNative _nativeStaticMetaObject; | |
466 public static QMetaObject staticMetaObject() { return _staticMetaObject; } | |
467 protected static void createStaticMetaObject() { | |
468 assert(!_staticMetaObject); | |
469 alias BaseClassesTuple!(typeof(this))[0] BaseClass; | |
470 if (!BaseClass._staticMetaObject) | |
471 BaseClass.createStaticMetaObject; | |
472 auto base = BaseClass._staticMetaObject; | |
473 _nativeStaticMetaObject = QMetaObjectNative(base.nativeId, qt_meta_stringdata_" ~ className ~ ".ptr, | |
474 qt_meta_data_" ~ className ~ ".ptr, null ); | |
475 | |
476 _staticMetaObject = new QMetaObject(&_nativeStaticMetaObject, base); | |
477 // _staticMetaObject.construct!(typeof(this)); | |
478 _populateMetaInfo(); | |
479 } | |
480 static this() | |
481 { | |
482 createStaticMetaObject(); | |
483 }\n\n"; | |
484 return res; | |
485 } | |
486 | |
487 size_t commaCount(int argCount) | |
488 { | |
489 size_t ret = 0; | |
490 if(argCount > 1) | |
491 ret = argCount - 1; | |
492 return ret; | |
493 } | |
494 | |
495 FunctionDef[] genFuncDefs(alias Funcs, alias newFunc)() | |
496 { | |
497 typeof(return) res; | |
498 enum funcsCount = Funcs.at.length; | |
499 foreach(i, bogus; Repeat!(void, funcsCount)) | |
500 { | |
501 string args = replicate(commaCount((MetaEntryArgs!(Funcs.at[i].at)).length), ','); | |
502 string funcSig = MetaEntryName!(Funcs.at[i].at) ~ "(" ~ qtDeclArgs!(MetaEntryArgs!(Funcs.at[i].at))() ~ ")"; | |
503 res ~= newFunc(funcSig, args); | |
504 } | |
505 return res; | |
506 } | |
507 | |
508 string generateMetaInfo(string className, alias Signals, alias Slots)() | |
509 { | |
510 string res = ""; | |
511 auto signalList = genFuncDefs!(Signals, newSignal)(); | |
512 auto slotList = genFuncDefs!(Slots, newSlot)(); | |
513 res ~= generateCode(className, signalList, slotList); | |
514 res ~= generate_qt_metacall!(Signals, Slots); | |
515 res ~= generateMetaObjectConstruction!(Signals, Slots); | |
516 res ~= generateQMetaObject(className); | |
517 return res; | |
518 } | |
519 | |
520 template Q_OBJECT_BIND() | |
521 { | |
522 mixin ("enum lastSignalIndex_" ~ typeof(this).stringof ~ " = " ~ toStringNow!(lastSignalIndex!(typeof(this))) ~ ";"); | |
523 } | |
524 | |
525 template Q_OBJECT() | |
526 { | |
527 // pragma(msg, toStringNow!(lastSignalIndex!(typeof(this)))); | |
528 mixin ("enum lastSignalIndex_" ~ typeof(this).stringof ~ " = " ~ toStringNow!(lastSignalIndex!(typeof(this))) ~ ";"); | |
529 | |
530 alias TupleWrapper!(findSymbols!(slotPrefix, typeof(this), ByOwner!(typeof(this)))) Slots; | |
531 alias TupleWrapper!(findSymbols!(signalPrefix, typeof(this), ByOwner!(typeof(this)))) Signals; | |
532 // pragma(msg, generateMetaInfo!((typeof(this)).stringof, Signals, Slots)()); | |
533 mixin(generateMetaInfo!((typeof(this)).stringof, Signals, Slots)()); | |
534 } |