Mercurial > projects > qtd
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qt/qtd/MOC.d Sun Nov 08 19:28:01 2009 +0000 @@ -0,0 +1,534 @@ +module qt.qtd.MOC; + +import qt.qtd.ctfe.Format; + +import std.typetuple; + +import qt.Signal; +import qt.qtd.MetaMarshall; + +public import std.traits; +/** + Utils. + */ + +T qMin(T)(T a,T b) { if (a < b) return a; return b; } +T qMax(T)(T a, T b) { if (a < b) return b; return a; } +T qBound(T)(T min, T val,T max) { return qMax(min, qMin(max, val)); } + +bool is_digit_char(const char s) +{ + return (s >= '0' && s <= '9'); +} + +bool is_octal_char(const char s) +{ + return (s >= '0' && s <= '7'); +} + +bool is_hex_char(const char s) +{ + return ((s >= 'a' && s <= 'f') + || (s >= 'A' && s <= 'F') + || (s >= '0' && s <= '9') + ); +} + +int lastIndexOf(T)(T[] haystack, T[] needle, int from = -1) +{ + auto l = haystack.length; + auto ol = needle.length; + int delta = l - ol; + if (from < 0) + from = delta; + if (from < 0 || from > l) + return -1; + if (from > delta) + from = delta; + + while(from >= 0) + { + if (haystack[from..from+ol] == needle) + return from; + from--; + } + return -1; +} + + +T[] newArray(T)(size_t len, T[] from = []) +{ + if (len == from.length) + return from; + + if (!from.length) + from = [T.init]; + + if (from.length < len) + return newArray!T(len, from ~ from); + + return from[0..len]; +} + +string replicate(int n, char value) +{ + char[] ret = "".dup; + if (n > 0) + { +// ret = newArray!char(n); + for(int i = 0; i < n; i++) + ret ~= value; + } + return cast(string)ret; +} + +template Repeat(T, int I) +{ + static if (!I) alias TypeTuple!() Repeat; + else alias TypeTuple!(T, Repeat!(T, I - 1)) Repeat; +} + +/** + CTFE MOC port. + */ + +enum MethodFlags { + AccessPrivate = 0x00, + AccessProtected = 0x01, + AccessPublic = 0x02, + MethodMethod = 0x00, + MethodSignal = 0x04, + MethodSlot = 0x08, + MethodConstructor = 0x0c, + MethodCompatibility = 0x10, + MethodCloned = 0x20, + MethodScriptable = 0x40 +} + +enum Access { Private, Protected, Public } + +struct FunctionDef +{ +/* FunctionDef(): returnTypeIsVolatile(false), access(Private), isConst(false), isVirtual(false), + inlineCode(false), wasCloned(false), isCompat(false), isInvokable(false), + isScriptable(false), isSlot(false), isSignal(false), + isConstructor(false), isDestructor(false), isAbstract(false) {} + */ +// Type type; +// string normalizedType; +// string tag; +// string name; + string sig; + string arguments; + Access access; +/* bool returnTypeIsVolatile; + + QList<ArgumentDef> arguments; + + enum Access { Private, Protected, Public }; + bool isConst; + bool isVirtual; + bool inlineCode; + bool wasCloned; + + QByteArray inPrivateClass; + bool isCompat; + bool isInvokable; + bool isScriptable; + bool isSlot; + bool isSignal; + bool isConstructor; + bool isDestructor; + bool isAbstract; + */ +} + +FunctionDef newSlot(string sig, string args) +{ + return FunctionDef(sig, args, Access.Public); +} + +FunctionDef newSignal(string sig, string args) +{ + return FunctionDef(sig, args, Access.Protected); +} + +struct Generator +{ + string output; + string[] strings; +// QByteArray purestSuperClass; +// QList<QByteArray> metaTypes; +} + +int lengthOfEscapeSequence(string s, uint i) +{ + if (s[i] != '\\' || i >= s.length - 1) + return 1; + const int startPos = i; + ++i; + auto ch = s[i]; + if (ch == 'x') { + ++i; + while (i < s.length && is_hex_char(s[i])) + ++i; + } else if (is_octal_char(ch)) { + while (i < startPos + 4 + && i < s.length + && is_octal_char(s[i])) { + ++i; + } + } else { // single character escape sequence + i = qMin(i + 1, s.length); + } + return i - startPos; +} + +int strreg(ref Generator gen, string s) +{ + int idx = 0; + foreach (str; gen.strings) { + if (str == s) + return idx; + idx += str.length + 1; + foreach (i, c; str) { + if (c == '\\') { + int cnt = lengthOfEscapeSequence(str, i) - 1; + idx -= cnt; + i += cnt; + } + } + } + gen.strings ~= s; + return idx; +} + +void generateFunctions(ref Generator gen, FunctionDef[] list, string functype, byte type) +{ + if (!list.length) + return; + gen.output ~= format_ctfe("\n // ${}s: signature, parameters, type, tag, flags\n", functype); + + foreach (i, f; list) { + byte flags = type; + + if (f.access == Access.Private) + flags |= MethodFlags.AccessPrivate; + else if (f.access == Access.Public) + flags |= MethodFlags.AccessPublic; + else if (f.access == Access.Protected) + flags |= MethodFlags.AccessProtected; + + gen.output ~= format_ctfe(" ${}, ${}, ${}, ${}, 0x${:x},\n", strreg(gen, f.sig), + strreg(gen, f.arguments), strreg(gen, ""/*f.normalizedType*/), strreg(gen, ""/*f.tag*/), flags); + } +} + +string generateCode(string className, FunctionDef[] signalList, FunctionDef[] slotList) +{ + auto gen = Generator("", []); + +/* bool isQt = (cdef->classname == "Qt"); + bool isQObject = (cdef->classname == "QObject"); + bool isConstructible = !cdef->constructorList.isEmpty(); + +// +// build the data array +// + int i = 0; + + + // filter out undeclared enumerators and sets + { + QList<EnumDef> enumList; + for (i = 0; i < cdef->enumList.count(); ++i) { + EnumDef def = cdef->enumList.at(i); + if (cdef->enumDeclarations.contains(def.name)) { + enumList += def; + } + QByteArray alias = cdef->flagAliases.value(def.name); + if (cdef->enumDeclarations.contains(alias)) { + def.name = alias; + enumList += def; + } + } + cdef->enumList = enumList; + } + + + QByteArray qualifiedClassNameIdentifier = cdef->qualified; + qualifiedClassNameIdentifier.replace(':', '_'); +*/ + bool isConstructible = false; + + FunctionDef[] propertyList, enumList, constructorList; + int index = 12; + gen.output ~= format_ctfe("static const uint[] qt_meta_data_${} = [\n", className); + gen.output ~= format_ctfe("\n // content:\n"); + gen.output ~= format_ctfe(" ${}, // revision\n", 2); + gen.output ~= format_ctfe(" ${}, // classname\n", strreg(gen, className)); + gen.output ~= format_ctfe(" ${}, ${}, // classinfo\n", 0, 0); +// index += cdef->classInfoList.count() * 2; + + int methodCount = signalList.length + slotList.length;// + cdef->methodList.count(); + gen.output ~= format_ctfe(" ${}, ${}, // methods\n", methodCount, methodCount ? index : 0); + index += methodCount * 5; + gen.output ~= format_ctfe(" ${}, ${}, // properties\n", propertyList.length, propertyList.length ? index : 0); + index += propertyList.length * 3; +// if(cdef->notifyableProperties) +// index += cdef->propertyList.count(); + gen.output ~= format_ctfe(" ${}, ${}, // enums/sets\n", enumList.length, enumList.length ? index : 0); + +// int enumsIndex = index; +// for (i = 0; i < cdef->enumList.count(); ++i) +// index += 4 + (cdef->enumList.at(i).values.count() * 2); + gen.output ~= format_ctfe(" ${}, ${}, // constructors\n", isConstructible ? constructorList.length : 0, + isConstructible ? index : 0); + +// +// Build classinfo array +// +// generateClassInfos(); + +// +// Build signals array first, otherwise the signal indices would be wrong +// + generateFunctions(gen, signalList, "signal", MethodFlags.MethodSignal); + +// +// Build slots array +// + generateFunctions(gen, slotList, "slot", MethodFlags.MethodSlot); + +// +// Build method array +// +// generateFunctions(cdef->methodList, "method", MethodMethod); + + +// +// Build property array +// +// generateProperties(); + +// +// Build enums array +// +// generateEnums(enumsIndex); + +// +// Build constructors array +// +// if (isConstructible) +// generateFunctions(cdef->constructorList, "constructor", MethodConstructor); + +// +// Terminate data array +// + gen.output ~= format_ctfe("\n 0 // eod\n];\n\n"); + +// +// Build stringdata array +// + gen.output ~= format_ctfe("static const string qt_meta_stringdata_${} = \n", className); + gen.output ~= format_ctfe(" \""); + int col = 0; + int len = 0; + foreach (i, s; gen.strings) { + len = s.length; + if (col && col + len >= 72) { + gen.output ~= format_ctfe("\"\n \""); + col = 0; + } else if (len && s[0] >= '0' && s[0] <= '9') { + gen.output ~= format_ctfe("\"\""); + len += 2; + } + int idx = 0; + while (idx < s.length) { + if (idx > 0) { + col = 0; + gen.output ~= format_ctfe("\"\n \""); + } + int spanLen = qMin(cast(uint)70, s.length - idx); + // don't cut escape sequences at the end of a line + int backSlashPos = s.lastIndexOf("\\", idx + spanLen - 1); + if (backSlashPos >= idx) { + int escapeLen = lengthOfEscapeSequence(s, backSlashPos); + spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, cast(int)(s.length - idx)); + } + gen.output ~= s[idx..idx+spanLen]; + idx += spanLen; + col += spanLen; + } + + gen.output ~= "\\0"; + col += len + 2; + } + gen.output ~= "\";\n\n"; + + return gen.output; +} + +string metaCallArgs(Args...)() +{ + string res; + foreach(i, _; Args) { + if (i > 0) + res ~= ","; + res ~= metaCallArgument!(Args[i])("_a[" ~ __toString(i+1) ~ "]"); + } + return res; +} + +string qtDeclArgs(Args...)() +{ + string ret; + foreach(i, _; Args) + { + if(i > 0) + ret ~= ","; + ret ~= qtDeclArg!(Args[i]); + } + return ret; +} + +string generate_qt_metacall(alias Signals, alias Slots)() +{ + string res = " +protected int qt_metacall(QMetaObject.Call _c, int _id, void **_a) + { + _id = super.qt_metacall(_c, _id, _a); + if (_id < 0) + return _id;\n"; + + alias TypeTuple!(Signals.at, Slots.at) Methods; + enum methodCount = Methods.length; + if(methodCount) + { + res ~= " + if (_c == QMetaObject.Call.InvokeMetaMethod) { + switch (_id) {"; + foreach(i, bogus; Repeat!(void, methodCount)) { + res ~= " + case " ~ __toString(i) ~ ": " ~ MetaEntryName!(Methods[i].at) ~ "(" ~ metaCallArgs!(MetaEntryArgs!(Methods[i].at))() ~ "); break;"; + } + res ~= "\n default: ;\n }\n"; + res ~= " _id -= " ~ __toString(methodCount) ~ ";"; + res ~= "\n }"; + } + + res ~= "\n return _id; + }"; + return res; +} + +string dDeclArgs(Args...)() +{ + string ret; + foreach(i, _; Args) + ret ~= ", " ~ Args[i].stringof; + return ret; +} +string genMetaMethodsConstr(alias Funcs)(string className) +{ + string res; + enum funcsCount = Funcs.at.length; + foreach(i, bogus; Repeat!(void, funcsCount)) + { + res ~= " index++;\n" ~ + " _staticMetaObject.addMethod(new " ~ className ~ "(signature!(\"" ~ MetaEntryName!(Funcs.at[i].at) ~ "\"" ~ dDeclArgs!(MetaEntryArgs!(Funcs.at[i].at))()~ "), index));\n\n"; + } + return res; +} +string generateMetaObjectConstruction(alias Signals, alias Slots)() +{ + string res; + res ~= "\n + private static void _populateMetaInfo() { + alias BaseClassesTuple!(typeof(this))[0] BaseClass; + int index = BaseClass.staticMetaObject().methodCount() - 1;\n\n"; + + res ~= genMetaMethodsConstr!(Signals)("QMetaSignal"); + res ~= genMetaMethodsConstr!(Slots)("QMetaSlot"); + + res ~= " + }\n"; + return res; +} + +string generateQMetaObject(string className) +{ + string res; + res ~= " + QMetaObject metaObject() { return staticMetaObject; } + private static QMetaObject _staticMetaObject; + private static QMetaObjectNative _nativeStaticMetaObject; + public static QMetaObject staticMetaObject() { return _staticMetaObject; } + protected static void createStaticMetaObject() { + assert(!_staticMetaObject); + alias BaseClassesTuple!(typeof(this))[0] BaseClass; + if (!BaseClass._staticMetaObject) + BaseClass.createStaticMetaObject; + auto base = BaseClass._staticMetaObject; + _nativeStaticMetaObject = QMetaObjectNative(base.nativeId, qt_meta_stringdata_" ~ className ~ ".ptr, + qt_meta_data_" ~ className ~ ".ptr, null ); + + _staticMetaObject = new QMetaObject(&_nativeStaticMetaObject, base); +// _staticMetaObject.construct!(typeof(this)); + _populateMetaInfo(); + } + static this() + { + createStaticMetaObject(); + }\n\n"; + return res; +} + +size_t commaCount(int argCount) +{ + size_t ret = 0; + if(argCount > 1) + ret = argCount - 1; + return ret; +} + +FunctionDef[] genFuncDefs(alias Funcs, alias newFunc)() +{ + typeof(return) res; + enum funcsCount = Funcs.at.length; + foreach(i, bogus; Repeat!(void, funcsCount)) + { + string args = replicate(commaCount((MetaEntryArgs!(Funcs.at[i].at)).length), ','); + string funcSig = MetaEntryName!(Funcs.at[i].at) ~ "(" ~ qtDeclArgs!(MetaEntryArgs!(Funcs.at[i].at))() ~ ")"; + res ~= newFunc(funcSig, args); + } + return res; +} + +string generateMetaInfo(string className, alias Signals, alias Slots)() +{ + string res = ""; + auto signalList = genFuncDefs!(Signals, newSignal)(); + auto slotList = genFuncDefs!(Slots, newSlot)(); + res ~= generateCode(className, signalList, slotList); + res ~= generate_qt_metacall!(Signals, Slots); + res ~= generateMetaObjectConstruction!(Signals, Slots); + res ~= generateQMetaObject(className); + return res; +} + +template Q_OBJECT_BIND() +{ + mixin ("enum lastSignalIndex_" ~ typeof(this).stringof ~ " = " ~ toStringNow!(lastSignalIndex!(typeof(this))) ~ ";"); +} + +template Q_OBJECT() +{ +// pragma(msg, toStringNow!(lastSignalIndex!(typeof(this)))); + mixin ("enum lastSignalIndex_" ~ typeof(this).stringof ~ " = " ~ toStringNow!(lastSignalIndex!(typeof(this))) ~ ";"); + + alias TupleWrapper!(findSymbols!(slotPrefix, typeof(this), ByOwner!(typeof(this)))) Slots; + alias TupleWrapper!(findSymbols!(signalPrefix, typeof(this), ByOwner!(typeof(this)))) Signals; +// pragma(msg, generateMetaInfo!((typeof(this)).stringof, Signals, Slots)()); + mixin(generateMetaInfo!((typeof(this)).stringof, Signals, Slots)()); +}