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