1 module dqml.qobjectgenerators; 2 3 import std.traits; 4 import std.algorithm; 5 import std.string; 6 import std.stdio; 7 8 struct QtPropertyData 9 { 10 public string type; 11 public string name; 12 public string read; 13 public string write; 14 public string notify; 15 } 16 17 struct QtProperty(T) 18 { 19 public QtPropertyData data; 20 21 this(string name, string read, string write, string notify) 22 { 23 data.type = T.stringof; 24 data.name = name; 25 data.read = read; 26 data.write = write; 27 data.notify = notify; 28 } 29 } 30 31 struct QtSlot {} 32 struct QtSignal {} 33 34 string GenerateVariantConversionCall(string typeName) 35 { 36 switch (typeName) 37 { 38 case "string": 39 return ".toString()"; 40 case "int": 41 return ".toInt()"; 42 case "bool": 43 return ".toBool()"; 44 case "float": 45 return ".toFloat()"; 46 case "double": 47 return ".toDouble()"; 48 case "QVariant": 49 return ""; 50 default: 51 throw new Exception("Unknown conversion function from Qvariant to " ~ typeName); 52 } 53 } 54 55 string GenerateArgumentList(string[] typeNames) 56 { 57 string result = ""; 58 for (int i = 0; i < typeNames.length; ++i) 59 { 60 auto typeName = typeNames[i]; 61 auto variantCall = GenerateVariantConversionCall(typeName); 62 result ~= i > 0 ? "," : ""; 63 result ~= format("arguments[%d]%s", i+1, variantCall); 64 } 65 return result; 66 } 67 68 string GenerateSlotCall(FunctionInfo info) 69 { 70 auto args = GenerateArgumentList(info.parameterTypes); 71 auto call = format("%s(%s)", info.name, args); 72 auto formatStr = info.returnType != "void" ? "arguments[0].setValue(%s)" : "%s"; 73 return format(formatStr, call); 74 } 75 76 string GenerateCaseBlock(FunctionInfo info) 77 { 78 string result = ""; 79 result ~= format("case \"%s\":\n", info.name); 80 result ~= format("%s;\n", GenerateSlotCall(info)); 81 result ~= "break;\n"; 82 return result; 83 } 84 85 string GenerateOnSlotCalled(QtInfo info) 86 { 87 string result = "protected override void onSlotCalled(QVariant slotName, QVariant[] arguments)\n"; 88 result ~= "{\n"; 89 result ~= "switch(slotName.toString())\n"; 90 result ~= "{\n"; 91 foreach (slot; info.slots) 92 result ~= GenerateCaseBlock(slot); 93 result ~= "default: super.onSlotCalled(slotName, arguments);\n"; 94 result ~= "}\n"; // 95 result ~= "}"; 96 return result; 97 } 98 99 string GenerateSignalCall(FunctionInfo info) 100 { 101 string args = ""; 102 string vars = ""; 103 for (int i = 0; i < info.parameterTypes.length; ++i) { 104 if (i > 0) { 105 args ~= ","; 106 vars ~= ","; 107 } 108 args ~= format("%s val%d", info.parameterTypes[i], i); 109 vars ~= format("val%d", i); 110 } 111 112 string result = format("pragma(mangle,\"%s\")\n", info.mangle); 113 result ~= format("public %s %s(%s) { emit(\"%s\", %s); }", info.returnType, info.name, args, info.name, vars); 114 return result; 115 } 116 117 string GenerateSignals(QtInfo info) 118 { 119 string result = ""; 120 foreach (signal; info.signals) 121 result ~= GenerateSignalCall(signal) ~ "\n"; 122 return result; 123 } 124 125 string GenerateMetaType(string typeName) 126 { 127 switch(typeName) 128 { 129 case "void": 130 return "QMetaType.Void"; 131 case "int": 132 return "QMetaType.Int"; 133 case "string": 134 return "QMetaType.String"; 135 case "QObject": 136 return "QMetaType.QObject"; 137 case "QVariant": 138 return "QMetaType.QVariant"; 139 case "bool": 140 return "QMetaType.Bool"; 141 case "float": 142 return "QMetaType.Float"; 143 case "double": 144 return "QMetaType.Double"; 145 default: 146 throw new Exception(format("Unknown conversion from %s to QMetaType", typeName)); 147 } 148 } 149 150 string GenerateParameterNamesList(FunctionInfo info) 151 { 152 string result = ""; 153 for (int i = 0; i < info.parameterNames.length; ++i) 154 { 155 if (i > 0) 156 result ~= ", "; 157 result ~= info.parameterNames[i]; 158 } 159 return result; 160 } 161 162 string GenerateMetaTypesListForSlot(FunctionInfo info) 163 { 164 string result = GenerateMetaType(info.returnType); 165 result ~= ", "; 166 result ~= GenerateMetaTypesListForSignal(info); 167 return result; 168 } 169 170 string GenerateMetaTypesListForSignal(FunctionInfo info) 171 { 172 string result = ""; 173 for (int i = 0; i < info.parameterTypes.length; ++i) 174 { 175 if (i > 0) 176 result ~= ", "; 177 result ~= GenerateMetaType(info.parameterTypes[i]); 178 } 179 return result; 180 } 181 182 struct FunctionInfo 183 { 184 string name; 185 string returnType; 186 string[] parameterNames; 187 string[] parameterTypes; 188 string mangle; 189 } 190 191 struct QtInfo 192 { 193 FunctionInfo[] slots; 194 FunctionInfo[] signals; 195 QtPropertyData[] properties; 196 } 197 198 public static QtInfo GetQtUDA(T)() 199 { 200 QtInfo result; 201 202 foreach (property; getUDAs!(T, QtProperty)) { 203 result.properties ~= property.data; 204 } 205 206 foreach (member; __traits(derivedMembers, T)) { 207 static if (__traits(compiles, __traits(getMember, T, member)) 208 && isSomeFunction!(__traits(getMember, T, member))) { 209 // Retrieve the UDA 210 auto attributes = __traits(getAttributes, __traits(getMember, T, member)); 211 212 // Turn the tuple in an array of strings 213 string[] attributeNames; 214 foreach (attribute; attributes) 215 attributeNames ~= typeof(attribute).stringof; 216 217 bool isSlot = attributeNames.canFind("QtSlot"); 218 bool isSignal = attributeNames.canFind("QtSignal"); 219 220 // Extract the Function Return Type and Arguments 221 if (isSlot || isSignal) { 222 FunctionInfo info; 223 info.mangle = __traits(getMember, T, member).mangleof; 224 info.name = member; 225 info.returnType = ReturnType!(__traits(getMember, T, member)).stringof; 226 227 foreach (param; ParameterIdentifierTuple!(__traits(getMember, T, member))) 228 info.parameterNames ~= param.stringof; 229 230 foreach (param; Parameters!(__traits(getMember, T, member))) 231 info.parameterTypes ~= param.stringof; 232 233 if (isSlot) 234 result.slots ~= info; 235 236 if (isSignal) 237 result.signals ~= info; 238 } 239 } 240 } 241 242 return result; 243 } 244 245 public static string GenerateMetaObject(string qobjectSuperClassName, QtInfo info) 246 { 247 string result = 248 "shared static this() { m_staticMetaObject = createMetaObject(); }\n" ~ 249 "private static QMetaObject m_staticMetaObject;\n" ~ 250 "public static QMetaObject staticMetaObject() { return m_staticMetaObject; }\n" ~ 251 "public override QMetaObject metaObject() { return staticMetaObject(); }\n"~ 252 "private static QMetaObject createMetaObject() {\n" ~ 253 " QMetaObject superMetaObject = " ~ qobjectSuperClassName ~ ".staticMetaObject();\n" ~ 254 " SignalDefinition[] signals = [];\n" ~ 255 " SlotDefinition[] slots = [];\n" ~ 256 " PropertyDefinition[] properties = [];\n"; 257 258 foreach(FunctionInfo signal; info.signals) { 259 string name = signal.name; 260 string parameterNames = GenerateParameterNamesList(signal); 261 string parameterTypes = GenerateMetaTypesListForSignal(signal); 262 result ~= format(" signals ~= SignalDefinition(\"%s\",[%s], [%s]);\n", signal.name, parameterNames, parameterTypes); 263 } 264 265 foreach(FunctionInfo slot; info.slots) { 266 string name = slot.name; 267 string returnType = GenerateMetaType(slot.returnType); 268 string parameterNames = GenerateParameterNamesList(slot); 269 string parameterTypes = GenerateMetaTypesListForSignal(slot); 270 result ~= format(" slots ~= SlotDefinition(\"%s\", %s, [%s], [%s]);\n", slot.name, returnType, parameterNames, parameterTypes); 271 } 272 273 foreach(QtPropertyData property; info.properties) { 274 string name = property.name; 275 string type = GenerateMetaType(property.type); 276 string read = property.read; 277 string write = property.write; 278 string notify = property.notify; 279 result ~= format(" properties ~= PropertyDefinition(\"%s\", %s, \"%s\", \"%s\", \"%s\");\n", name, type, read, write, notify); 280 } 281 282 result ~= 283 " return new QMetaObject(superMetaObject, typeof(this).stringof, signals, slots, properties);\n}\n"; 284 return result; 285 } 286 287 public static string QObjectSuperClass(T)() 288 { 289 foreach (Type; BaseClassesTuple!T) { 290 static if (__traits(compiles, Type.staticMetaObject())) { 291 return Type.stringof; 292 } 293 } 294 } 295 296 public mixin template Q_OBJECT() 297 { 298 private static string GenerateCode() 299 { 300 alias outerType = typeof(this); 301 alias info = GetQtUDA!outerType; 302 string result; 303 result ~= GenerateMetaObject(QObjectSuperClass!outerType, info); 304 result ~= GenerateOnSlotCalled(info); 305 result ~= GenerateSignals(info); 306 return result; 307 } 308 mixin(GenerateCode); 309 }