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 }