1 /**
2  * Method binding - before alias inference
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.meta.binding_v1;
15 
16 import std.traits;
17 
18 import ae.utils.meta.caps;
19 import ae.utils.meta.misc;
20 import ae.utils.meta.proxy;
21 import ae.utils.meta.reference;
22 
23 /// Disconnect function (or function template) f from its "this" pointer,
24 /// creating a template that can be passed as an alias parameter
25 /// to a template which already has a context (such as a non-static
26 /// templated method).
27 /// To connect the alias back to a "this" pointer, use .connect(p).
28 /// Use .call(args) on the result to call the resulting function.
29 /// Requires __traits(child) support.
30 template disconnect(alias f)
31 {
32 	static assert(haveChildTrait, "Your compiler doesn't support __traits(child)");
33 
34 	alias P = thisOf!f;
35 	alias R = RefType!P;
36 
37 	struct disconnect
38 	{
39 		R p;
40 
41 		@disable this();
42 		private this(R p) { this.p = p; }
43 
44 		static typeof(this) connect(R p) { return typeof(this)(p); }
45 
46 		auto call(T...)(auto ref T args)
47 		{
48 			return __traits(child, p, f)(args);
49 		}
50 	}
51 }
52 
53 // (illustration for why disconnect is needed)
54 version(none)
55 {
56 	struct X
57 	{
58 		int i;
59 
60 		void funB(/* ??? */)(/* ??? */)
61 		{
62 			c(i);
63 		}
64 	}
65 
66 	struct Y
67 	{
68 		X* x;
69 
70 		void funA()
71 		{
72 			// Problem: pass funC to x.funB so that funB can call it.
73 			// funC is a templated method, and Y doesn't know
74 			// how X will instantiate it beforehand.
75 
76 			x.funB!funC(); // Doesn't work - X doesn't have an Y* and it
77 			               // is not transmitted via the alias parameter
78 
79 			x.funB(&funC); // Doesn't work - can't take the address of a
80 			               // template declaration
81 
82 			/* ??? */
83 		}
84 
85 		void funC(T)(T v)
86 		{
87 			// ...
88 		}
89 	}
90 }
91 
92 static if (haveChildTrait)
93 unittest
94 {
95 	static struct A()
96 	{
97 		static template Impl(alias v)
98 		{
99 			void call(T)(T target)
100 			{
101 				target.callee!(disconnect!call2)();
102 			}
103 
104 			void call2(T)(T target)
105 			{
106 				target.setter();
107 			}
108 		}
109 	}
110 
111 	static struct B(alias a)
112 	{
113 		static template Impl(alias v)
114 		{
115 			void doCall()
116 			{
117 				alias RefType!(typeof(this)) Parent;
118 				static struct Target
119 				{
120 					int i;
121 					Parent parent;
122 
123 					void callee(alias setter)()
124 					{
125 						setter.connect(parent).call(&this);
126 					}
127 
128 					void setter()
129 					{
130 						i = 42;
131 					}
132 				}
133 
134 				Target target;
135 				target.parent = this.reference;
136 				a.call(&target);
137 				v = target.i;
138 			}
139 		}
140 	}
141 
142 	static struct S
143 	{
144 		int v;
145 		alias a = A!( ).Impl!v;
146 		alias b = B!(a).Impl!v;
147 	}
148 
149 	S s;
150 	s.b.doCall();
151 	assert(s.v == 42);
152 }
153 
154 // ***************************************************************************
155 
156 /// This function group manufactures delegate-like objects which selectively
157 /// contain a context or function pointer. This allows avoiding the overhead
158 /// of an indirect call (which prevents inlining), or passing around a context
159 /// when that context is already available on the caller's side, all while
160 /// having the same syntax for invocation. The invocation syntax is as follows:
161 /// If fun is the return value of these functions, and Fun is its type,
162 /// Fun.call(fun, args...) will invoke the respective function.
163 /// Unbound variants provide a callWith method, which additionally take a
164 /// context to rebind with.
165 
166 /// Unbound delegate alias.
167 /// Return value contains nothing (empty struct).
168 /// Example construction: $(D unboundDgAlias!method)
169 @property auto unboundDgAlias(alias fun)()
170 {
171 	return UnboundDgAlias!fun();
172 }
173 struct UnboundDgAlias(alias fun)
174 {
175 	alias C = RefType!(thisOf!fun);
176 
177 	static template Caller(alias fun)
178 	{
179 		auto Caller(Args...)(UnboundDgAlias self, auto ref Args args)
180 		{
181 			return fun(args);
182 		}
183 	}
184 
185 	/// Call the delegate using the context from the caller's context.
186 	alias call = Caller!fun;
187 
188 	/// Call the delegate using the given context.
189 	static auto callWith(Args...)(UnboundDgAlias self, C context, auto ref Args args)
190 	{
191 		return __traits(child, context, fun)(args);
192 	}
193 }
194 
195 /// Bound delegate alias.
196 /// Return value contains context.
197 /// Example construction: $(D boundDgAlias!method(context))
198 template boundDgAlias(alias fun)
199 {
200 	static auto boundDgAlias(RefType!(thisOf!fun) context)
201 	{
202 		return BoundDgAlias!fun(context);
203 	}
204 }
205 struct BoundDgAlias(alias fun)
206 {
207 	alias C = RefType!(thisOf!fun);
208 	C context;
209 
210 	/// Call the delegate using the stored context.
211 	static auto call(Args...)(BoundDgAlias self, auto ref Args args)
212 	{
213 		auto c = self.context;
214 		return __traits(child, c, fun)(args);
215 	}
216 
217 	// HACK, TODO
218 	static auto callWith(C, Args...)(BoundDgAlias self, C context, auto ref Args args)
219 	{
220 		auto c = self.context;
221 		return __traits(child, c, fun)(args);
222 	}
223 }
224 
225 /// Unbound delegate pointer.
226 /// Return value contains function pointer without context.
227 /// Example construction: $(D unboundDgPointer!method(&method))
228 /// Currently only implements callWith.
229 template unboundDgPointer(alias fun)
230 {
231 	static auto unboundDgPointer(Dg)(Dg dg)
232 	{
233 		return UnboundDgPointer!(RefType!(thisOf!fun), Dg)(dg);
234 	}
235 }
236 struct UnboundDgPointer(C, Dg)
237 {
238 	typeof(Dg.init.funcptr) func;
239 
240 	this(Dg dg)
241 	{
242 		func = dg.funcptr;
243 	}
244 
245 	static auto callWith(Args...)(UnboundDgPointer self, C context, auto ref Args args)
246 	{
247 		Dg dg;
248 		dg.ptr = context;
249 		dg.funcptr = self.func;
250 		return dg(args);
251 	}
252 }
253 
254 /// Bound delegate pointer.
255 /// Just a regular D delegate, basically.
256 /// Return value contains a D delegate.
257 /// Example construction: $(D boundDgPointer(&method))
258 auto boundDgPointer(Dg)(Dg dg)
259 {
260 	return BoundDgPointer!Dg(dg);
261 }
262 struct BoundDgPointer(Dg)
263 {
264 	Dg dg;
265 
266 	static auto call(Args...)(BoundDgPointer self, auto ref Args args)
267 	{
268 		return self.dg(args);
269 	}
270 }
271 
272 static if (haveMethodAliasBinding)
273 unittest
274 {
275 	static struct A
276 	{
277 		static template Impl(alias anchor)
278 		{
279 			void caller(Fun)(Fun fun)
280 			{
281 			//	pragma(msg, Fun.sizeof);
282 				Fun.call(fun);
283 			}
284 
285 			void callerIndirect(Fun, C)(Fun fun, C c)
286 			{
287 				Fun.callWith(fun, c);
288 			}
289 		}
290 	}
291 
292 	static struct B(alias a)
293 	{
294 		static template Impl(alias anchor)
295 		{
296 			void test()
297 			{
298 				a.caller(unboundDgAlias!calleeB);
299 				a.callerIndirect(unboundDgAlias!calleeB, this.reference);
300 
301 				C c;
302 				a.caller(boundDgAlias!(C.calleeC)(c.reference));
303 
304 				a.callerIndirect(unboundDgPointer!(c.calleeC)(&c.calleeC), c.reference);
305 
306 				a.caller(boundDgPointer(&c.calleeC));
307 			}
308 
309 			void calleeB()
310 			{
311 				anchor = 42;
312 			}
313 
314 			struct C
315 			{
316 				int value;
317 
318 				void calleeC()
319 				{
320 					value = 42;
321 				}
322 			}
323 		}
324 	}
325 
326 	static struct Test
327 	{
328 		int anchor;
329 		alias A.Impl!anchor a;
330 		alias B!a.Impl!anchor b;
331 	}
332 
333 	Test test;
334 	test.b.test();
335 }
336 
337 // ***************************************************************************
338 
339 /// Equilavents to the above functions, but for entire objects.
340 /// Caller syntax:
341 /// Obj.method(obj, args...)
342 
343 /// Create an unbound "object" which forwards calls to the given alias.
344 /// Context is inferred from caller site.
345 /// Example construction: unboundObj!aggregate
346 @property auto unboundObj(alias target)()
347 {
348 	return UnboundObj!target();
349 }
350 struct UnboundObj(alias target)
351 {
352 	template opDispatch(string name)
353 	{
354 		static template funTpl(alias fun)
355 		{
356 			auto funTpl(Args...)(UnboundObj!target self, auto ref Args args)
357 			{
358 				return fun(args);
359 			}
360 		}
361 
362 		mixin(`alias opDispatch = funTpl!(target.`~name~`);`);
363 	}
364 }
365 
366 /// Create a bound "object" with the given context.
367 /// Example construction: boundObj(context)
368 auto boundObj(S)(S s)
369 {
370 	return DispatchToFirstArg!S(s);
371 }
372 
373 /// Create a bound "object" with the given sub-aggregate and context.
374 /// Example construction: boundObjScope!aggregate(context)
375 auto boundObjScope(alias obj, S)(S s)
376 {
377 	alias BoundProxy = ScopeProxy!obj;
378 	return DispatchToFirstArg!BoundProxy(BoundProxy(s));
379 }
380 
381 struct DispatchToFirstArg(T)
382 {
383 	T next;
384 
385 	template opDispatch(string name)
386 	{
387 		static auto opDispatch(Args...)(DispatchToFirstArg self, auto ref Args args)
388 		{
389 			return mixin("self.next." ~ name ~ "(args)");
390 		}
391 	}
392 }
393 
394 static if (haveMethodAliasBinding)
395 unittest
396 {
397 	static struct Consumer
398 	{
399 		static template Impl(alias anchor)
400 		{
401 			void caller(Obj)(Obj obj)
402 			{
403 				Obj.callee(obj);
404 			}
405 		}
406 	}
407 
408 	static struct AliasTarget(alias consumer)
409 	{
410 		static template Impl(alias anchor)
411 		{
412 			void test()
413 			{
414 				consumer.caller(unboundObj!(Impl!anchor));
415 				consumer.caller(boundObjScope!(Impl!anchor)(this.reference));
416 
417 				static struct Test
418 				{
419 					void callee() {}
420 				}
421 				Test test;
422 				consumer.caller(boundObj(&test));
423 			}
424 
425 			void callee() {}
426 		}
427 	}
428 
429 	static struct Test
430 	{
431 		int anchor;
432 		alias Consumer.Impl!anchor c;
433 		alias AliasTarget!c.Impl!anchor t;
434 
435 		void test()
436 		{
437 			t.test();
438 		}
439 	}
440 
441 	Test test;
442 	test.test();
443 }