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 }