1 module ingen; 2 3 import std.typecons; 4 5 interface Injectable { 6 } 7 8 enum InGenEnums { 9 Singleton 10 } 11 12 struct InGen { 13 static auto opCall(T...)(T args) { 14 return tuple(InGenEnums.Singleton, args); 15 } 16 } 17 18 private { 19 T emplace(T, Args...)(void[] chunk, auto ref Args args) 20 if (is(T == class)) 21 { 22 import std.traits; 23 enum classSize = __traits(classInstanceSize, T); 24 testEmplaceChunk(chunk, classSize, classInstanceAlignment!T, T.stringof); 25 auto result = cast(T) chunk.ptr; 26 27 // Initialize the object in its pre-ctor state 28 chunk[0 .. classSize] = typeid(T).init[]; 29 InGenFactory.makeClass(result, args); 30 31 // Call the ctor if any 32 static if (is(typeof(result.__ctor(args)))) 33 { 34 // T defines a genuine constructor accepting args 35 // Go the classic route: write .init first, then call ctor 36 result.__ctor(args); 37 } 38 else 39 { 40 static assert(args.length == 0 && !is(typeof(&T.__ctor)), 41 "Don't know how to initialize an object of type " 42 ~ T.stringof ~ " with arguments " ~ Args.stringof); 43 } 44 return result; 45 } 46 47 T* emplace(T, Args...)(void[] chunk, auto ref Args args) 48 if (!is(T == class)) 49 { 50 testEmplaceChunk(chunk, T.sizeof, T.alignof, T.stringof); 51 return emplace(cast(T*) chunk.ptr, args); 52 } 53 54 package ref UT emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args) 55 if (is(UT == Unqual!UT)) 56 { 57 return emplaceImpl!UT(chunk, args); 58 } 59 // ditto 60 package ref UT emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args) 61 if (is(UT == Unqual!T) && !is(T == UT)) 62 { 63 return emplaceImpl!T(chunk, args); 64 } 65 66 67 private template emplaceImpl(T) 68 { 69 alias UT = Unqual!T; 70 71 ref UT emplaceImpl()(ref UT chunk) 72 { 73 static assert (is(typeof({static T i;})), 74 convFormat("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof)); 75 76 return emplaceInitializer(chunk); 77 } 78 79 static if (!is(T == struct)) 80 ref UT emplaceImpl(Arg)(ref UT chunk, auto ref Arg arg) 81 { 82 static assert(is(typeof({T t = arg;})), 83 convFormat("%s cannot be emplaced from a %s.", T.stringof, Arg.stringof)); 84 85 static if (isStaticArray!T) 86 { 87 alias UArg = Unqual!Arg; 88 alias E = ElementEncodingType!(typeof(T.init[])); 89 alias UE = Unqual!E; 90 enum n = T.length; 91 92 static if (is(Arg : T)) 93 { 94 //Matching static array 95 static if (!hasElaborateAssign!UT && isAssignable!(UT, Arg)) 96 chunk = arg; 97 else static if (is(UArg == UT)) 98 { 99 import core.stdc.string : memcpy; 100 // This is known to be safe as the two values are the same 101 // type and the source (arg) should be initialized 102 () @trusted { memcpy(&chunk, &arg, T.sizeof); }(); 103 static if (hasElaborateCopyConstructor!T) 104 _postblitRecurse(chunk); 105 } 106 else 107 .emplaceImpl!T(chunk, cast(T)arg); 108 } 109 else static if (is(Arg : E[])) 110 { 111 //Matching dynamic array 112 static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg[]))) 113 chunk[] = arg[]; 114 else static if (is(Unqual!(ElementEncodingType!Arg) == UE)) 115 { 116 import core.stdc.string : memcpy; 117 assert(n == chunk.length, "Array length missmatch in emplace"); 118 119 // This is unsafe as long as the length match is a 120 // precondition and not an unconditional exception 121 memcpy(&chunk, arg.ptr, T.sizeof); 122 123 static if (hasElaborateCopyConstructor!T) 124 _postblitRecurse(chunk); 125 } 126 else 127 .emplaceImpl!T(chunk, cast(E[])arg); 128 } 129 else static if (is(Arg : E)) 130 { 131 //Case matching single element to array. 132 static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg))) 133 chunk[] = arg; 134 else static if (is(UArg == Unqual!E)) 135 { 136 import core.stdc.string : memcpy; 137 138 foreach(i; 0 .. n) 139 { 140 // This is known to be safe as the two values are the same 141 // type and the source (arg) should be initialized 142 () @trusted { memcpy(&(chunk[i]), &arg, E.sizeof); }(); 143 } 144 145 static if (hasElaborateCopyConstructor!T) 146 _postblitRecurse(chunk); 147 } 148 else 149 //Alias this. Coerce. 150 .emplaceImpl!T(chunk, cast(E)arg); 151 } 152 else static if (is(typeof(.emplaceImpl!E(chunk[0], arg)))) 153 { 154 //Final case for everything else: 155 //Types that don't match (int to uint[2]) 156 //Recursion for multidimensions 157 static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg))) 158 chunk[] = arg; 159 else 160 foreach(i; 0 .. n) 161 .emplaceImpl!E(chunk[i], arg); 162 } 163 else 164 static assert(0, convFormat("Sorry, this implementation doesn't know how to emplace a %s with a %s", T.stringof, Arg.stringof)); 165 166 return chunk; 167 } 168 else 169 { 170 chunk = arg; 171 return chunk; 172 } 173 } 174 } 175 176 private void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName) @nogc pure nothrow 177 { 178 assert(chunk.length >= typeSize, "emplace: Chunk size too small."); 179 assert((cast(size_t)chunk.ptr) % typeAlignment == 0, "emplace: Chunk is not aligned."); 180 } 181 182 //emplace helper functions 183 private ref T emplaceInitializer(T)(ref T chunk) @trusted pure nothrow 184 { 185 static if (!hasElaborateAssign!T && isAssignable!T) 186 chunk = T.init; 187 else 188 { 189 import core.stdc.string : memcpy; 190 static immutable T init = T.init; 191 memcpy(&chunk, &init, T.sizeof); 192 } 193 return chunk; 194 } 195 196 T* emplace(T)(T* chunk) @safe pure nothrow 197 { 198 emplaceImpl!T(*chunk); 199 return chunk; 200 } 201 202 T* emplace(T, Args...)(T* chunk, auto ref Args args) 203 if (!is(T == struct) && Args.length == 1) 204 { 205 emplaceImpl!T(*chunk, args); 206 return chunk; 207 } 208 /// ditto 209 T* emplace(T, Args...)(T* chunk, auto ref Args args) 210 if (is(T == struct)) 211 { 212 emplaceImpl!T(*chunk, args); 213 return chunk; 214 } 215 } 216 217 class InGenFactory { 218 import std.traits; 219 import std.stdio; 220 import std.array : appender; 221 222 static auto make(T,A...)(auto ref A args) { 223 static if(isInterface!T) { 224 return InGenFactory.makeInterface!T(args); 225 } else { 226 import core.memory : GC; 227 auto mem = new void[__traits(classInstanceSize, T)]; 228 return ingenEmplace!T(mem, args); 229 } 230 } 231 232 static T get(T)() { 233 enum s = buildString!T(tuple(InGenEnums.Singleton)); 234 if(auto tmp = s in InGenFactory.instances) { 235 return cast(T)*tmp; 236 } 237 238 throw new Exception("Requested instance of Object " ~ T.stringof ~ 239 " does not exists." 240 ); 241 } 242 243 static void register(Inter,Impl)() { 244 InGenFactory.interfaces[typeid(Inter)] = Impl.classinfo; 245 } 246 247 static void inject(T)(ref T cls) { 248 makeClass(cls); 249 } 250 251 private: 252 253 static T ingenEmplace(T, Args...)(ref void[] chunk, auto ref Args args) 254 @trusted if (is(T == class)) 255 { 256 T result = emplace!T(chunk, args); 257 return result; 258 } 259 260 static Injectable[string] instances; 261 static TypeInfo_Class[TypeInfo] interfaces; 262 263 static auto makeInterface(T,A...)(auto ref A args) { 264 return InGenFactory.interfaces[typeid(T)].create(args); 265 } 266 267 static void makeClass(T,A...)(ref T ret, auto ref A args) { 268 foreach(it; __traits(allMembers, T)) { 269 foreach(jt; __traits(getAttributes, __traits(getMember, T, it))) { 270 static if(isTuple!(typeof(jt))) { 271 alias Type = typeof(__traits(getMember, T, it)); 272 if(jt.length > 0 && jt[0] == InGenEnums.Singleton) { 273 string s = buildString!Type(jt); 274 if(auto tmp = s in InGenFactory.instances) { 275 __traits(getMember, ret, it) = cast(Type)*tmp; 276 } else { 277 auto tmp = InGenFactory.make!Type(jt[1 .. $]); 278 InGenFactory.instances[s] = tmp; 279 __traits(getMember, ret, it) = tmp; 280 } 281 } 282 } 283 } 284 } 285 } 286 287 template isInterface(T) 288 { 289 enum isInterface = is(T == interface); 290 } 291 292 static string buildString(T,A)(A a) { 293 return T.stringof ~ "(" ~ a.toString() ~ ")"; 294 } 295 } 296 297 version(unittest) { 298 299 /** Everything that should be injected must be a class and implement the 300 interface Injectable. The implementation is trivial as it is an empty 301 interface. 302 */ 303 class TestInj1 : Injectable { 304 int a; 305 string someString; 306 this(int a) @safe { 307 this.a = a; 308 } 309 } 310 311 class TestInj2 : Injectable { 312 string s; 313 this(string s) @safe { 314 this.s = s; 315 } 316 } 317 318 class TestInj3 : Injectable { 319 this() { } 320 } 321 322 struct TestStruct { 323 int[] a; 324 string f; 325 } 326 327 //enum InGen; 328 329 class TestClass : Injectable { 330 /** InGen(10) will check if there is already an instance of TestInj1 which 331 was constructed with the value 10. If it exists, it is returned. 332 Otherwise a TestInj1 is created with 10 passed to its constructor. 333 This created instance is additionally stored inside the InGenFactory. 334 */ 335 @InGen(10) TestInj1 inj1; 336 int notInjected; 337 @InGen("Foo") TestInj2 inj2; 338 @InGen() TestInj3 inj3; 339 340 TestStruct ts; 341 342 this() { 343 assert(this.inj1 !is null); 344 assert(this.inj2 !is null); 345 assert(this.inj3 !is null); 346 } 347 } 348 349 class TestClass2 { 350 TestInj1 inj1; 351 } 352 353 class TestClass3 { 354 @InGen() TestClass tc; 355 this() { 356 assert(this.tc !is null); 357 assert(this.tc.inj1 !is null); 358 assert(this.tc.inj2 !is null); 359 assert(this.tc.inj3 !is null); 360 } 361 } 362 363 interface SomeInterface { 364 } 365 366 class InterImpl : SomeInterface { 367 368 } 369 370 } 371 372 unittest { 373 InGenFactory.register!(SomeInterface, InterImpl)(); 374 auto ii = InGenFactory.make!SomeInterface(); 375 InterImpl iim = cast(InterImpl)ii; 376 InGenFactory.inject(iim); 377 assert(iim !is null); 378 } 379 380 unittest { 381 import std.array : empty; 382 auto tc = InGenFactory.make!TestClass(); 383 assert(tc.inj1.a == 10); 384 assert(tc.inj2.s == "Foo"); 385 assert(tc.ts.a.empty); 386 assert(tc.ts.a == ""); 387 388 auto tc_2 = InGenFactory.make!TestClass(); 389 assert(tc_2.inj1.a == 10); 390 assert(tc_2.inj2.s == "Foo"); 391 392 assert(tc.inj1 is tc_2.inj1); 393 assert(tc.inj2 is tc_2.inj2); 394 assert(tc.inj3 is tc_2.inj3); 395 396 auto tc2 = InGenFactory.make!TestClass2(); 397 assert(tc2.inj1 is null); 398 399 auto tc2_2 = InGenFactory.make!TestClass2(); 400 assert(tc2_2.inj1 is null); 401 assert(tc2 !is tc2_2); 402 403 tc = InGenFactory.make!TestClass(); 404 405 assert(tc.inj1.a == 10); 406 assert(tc.inj2.s == "Foo"); 407 assert(tc.inj3 !is null); 408 409 /** get allows to get a existing instance of a class or interface. 410 In case the requested type does not exists an Exception will be thrown. 411 */ 412 auto ti3 = InGenFactory.get!TestInj3(); 413 assert(ti3 !is null); 414 415 auto ti3_2 = InGenFactory.get!TestInj3(); 416 assert(ti3 is ti3_2); 417 418 import std.exception : assertThrown; 419 assertThrown(InGenFactory.get!TestInj2()); 420 } 421 422 unittest { 423 auto tc = InGenFactory.make!TestClass3(); 424 assert(tc !is null); 425 assert(tc.tc !is null); 426 assert(tc.tc.inj1 !is null); 427 assert(tc.tc.inj1.a == 10); 428 assert(tc.tc.inj2 !is null); 429 assert(tc.tc.inj2.s == "Foo"); 430 assert(tc.tc.inj3 !is null); 431 432 auto tc2 = InGenFactory.make!TestClass3(); 433 assert(tc2 !is null); 434 assert(tc2.tc !is null); 435 assert(tc2.tc.inj1 !is null); 436 assert(tc2.tc.inj1.a == 10); 437 assert(tc2.tc.inj2 !is null); 438 assert(tc2.tc.inj2.s == "Foo"); 439 assert(tc2.tc.inj3 !is null); 440 441 assert(tc.tc is tc2.tc); 442 assert(tc.tc.inj1 is tc2.tc.inj1); 443 assert(tc.tc.inj2 is tc2.tc.inj2); 444 assert(tc.tc.inj3 is tc2.tc.inj3); 445 }