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 GenerateMetaTypesListForSlot(FunctionInfo info) 151 { 152 string result = GenerateMetaType(info.returnType); 153 result ~= ", "; 154 result ~= GenerateMetaTypesListForSignal(info); 155 return result; 156 } 157 158 string GenerateMetaTypesListForSignal(FunctionInfo info) 159 { 160 string result = ""; 161 for (int i = 0; i < info.parameterTypes.length; ++i) 162 { 163 if (i > 0) 164 result ~= ", "; 165 result ~= GenerateMetaType(info.parameterTypes[i]); 166 } 167 return result; 168 } 169 170 struct FunctionInfo 171 { 172 string name; 173 string returnType; 174 string[] parameterTypes; 175 string mangle; 176 } 177 178 struct QtInfo 179 { 180 FunctionInfo[] slots; 181 FunctionInfo[] signals; 182 QtPropertyData[] properties; 183 } 184 185 public static QtInfo GetQtUDA(T)() 186 { 187 QtInfo result; 188 189 foreach (property; getUDAs!(T, QtProperty)) { 190 result.properties ~= property.data; 191 } 192 193 foreach (member; __traits(derivedMembers, T)) { 194 static if (__traits(compiles, __traits(getMember, T, member)) 195 && isSomeFunction!(__traits(getMember, T, member))) { 196 // Retrieve the UDA 197 auto attributes = __traits(getAttributes, __traits(getMember, T, member)); 198 199 // Turn the tuple in an array of strings 200 string[] attributeNames; 201 foreach (attribute; attributes) 202 attributeNames ~= typeof(attribute).stringof; 203 204 bool isSlot = attributeNames.canFind("QtSlot"); 205 bool isSignal = attributeNames.canFind("QtSignal"); 206 207 // Extract the Function Return Type and Arguments 208 if (isSlot || isSignal) { 209 FunctionInfo info; 210 info.mangle = __traits(getMember, T, member).mangleof; 211 info.name = member; 212 info.returnType = ReturnType!(__traits(getMember, T, member)).stringof; 213 214 foreach (param; ParameterTypeTuple!(__traits(getMember, T, member))) 215 info.parameterTypes ~= param.stringof; 216 217 if (isSlot) 218 result.slots ~= info; 219 220 if (isSignal) 221 result.signals ~= info; 222 } 223 } 224 } 225 226 return result; 227 } 228 229 public static string GenerateMetaObject(string qobjectSuperClassName, QtInfo info) 230 { 231 string result = 232 "shared static this() { m_staticMetaObject = createMetaObject(); }\n" ~ 233 "private static QMetaObject m_staticMetaObject;\n" ~ 234 "public static QMetaObject staticMetaObject() { return m_staticMetaObject; }\n" ~ 235 "public override QMetaObject metaObject() { return staticMetaObject(); }\n"~ 236 "private static QMetaObject createMetaObject() {\n" ~ 237 " QMetaObject superMetaObject = " ~ qobjectSuperClassName ~ ".staticMetaObject();\n" ~ 238 " SignalDefinition[] signals = [];\n" ~ 239 " SlotDefinition[] slots = [];\n" ~ 240 " PropertyDefinition[] properties = [];\n"; 241 242 foreach(FunctionInfo signal; info.signals) { 243 result ~= format(" signals ~= SignalDefinition(\"%s\",[%s]);\n", signal.name, GenerateMetaTypesListForSignal(signal)); 244 } 245 246 foreach(FunctionInfo slot; info.slots) { 247 string name = slot.name; 248 string returnType = GenerateMetaType(slot.returnType); 249 string parameters = GenerateMetaTypesListForSignal(slot); 250 result ~= format(" slots ~= SlotDefinition(\"%s\", %s, [%s]);\n", slot.name, returnType, parameters); 251 } 252 253 foreach(QtPropertyData property; info.properties) { 254 string name = property.name; 255 string type = GenerateMetaType(property.type); 256 string read = property.read; 257 string write = property.write; 258 string notify = property.notify; 259 result ~= format(" properties ~= PropertyDefinition(\"%s\", %s, \"%s\", \"%s\", \"%s\");\n", name, type, read, write, notify); 260 } 261 262 result ~= 263 " return new QMetaObject(superMetaObject, typeof(this).stringof, signals, slots, properties);\n}\n"; 264 return result; 265 } 266 267 public static string QObjectSuperClass(T)() 268 { 269 foreach (Type; BaseClassesTuple!T) { 270 static if (__traits(compiles, Type.staticMetaObject())) { 271 return Type.stringof; 272 } 273 } 274 } 275 276 public mixin template Q_OBJECT() 277 { 278 private static string GenerateCode() 279 { 280 alias outerType = typeof(this); 281 alias info = GetQtUDA!outerType; 282 string result; 283 result ~= GenerateMetaObject(QObjectSuperClass!outerType, info); 284 result ~= GenerateOnSlotCalled(info); 285 result ~= GenerateSignals(info); 286 return result; 287 } 288 mixin(GenerateCode); 289 }