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 }