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