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 }