1 /// 2 module aslike; 3 4 import std.traits; 5 import std.meta; 6 import std.algorithm : canFind; 7 8 private string refPref(alias fn)() 9 { return functionAttributes!fn & FunctionAttribute.ref_ ? "ref" : ""; } 10 11 private string fnAttr(alias fn, string[] forbidden=[])() 12 { 13 enum f = forbidden ~ "ref"; 14 15 template impl(string[] s) 16 { 17 static if (s.length == 1) enum impl = f.canFind(s[0]) ? "" : s[0]; 18 else enum impl = impl!(s[0..$/2]) ~ " " ~ impl!(s[$/2..$]); 19 } 20 21 return impl!([__traits(getFunctionAttributes, fn)]); 22 } 23 24 /// 25 struct Like(T) if (is(T == interface)) 26 { 27 private template dlgName(alias fn) { enum dlgName = "__dlg_" ~ fn.mangleof; } 28 29 private static string buildDelegate(alias fn)() 30 { 31 enum a1 = dlgName!fn~"_type"; 32 enum a2 = refPref!fn ~ " ReturnType!fn"; 33 enum a3 = fnAttr!(fn, ["const", "@property"]); 34 enum a4 = dlgName!fn; 35 return "private alias "~a1~" = "~a2~" delegate(Parameters!fn) "~a3~"; 36 private "~a1~" "~a4~";\n"; 37 } 38 39 private static string buildCall(alias fn, string name)() 40 { 41 enum a2 = refPref!fn; 42 enum a3 = name; 43 enum a4 = fnAttr!fn; 44 enum a5 = dlgName!fn; 45 enum a6 = dlgName!fn~"_args"; 46 return a2~" ReturnType!fn "~a3~"(Parameters!fn "~a6~") "~a4~ 47 " { return "~a5~"("~a6~"); }\n"; 48 } 49 50 alias IT = InterfacesTuple!T; 51 static if (IT.length) 52 { 53 static foreach (it; IT) 54 { 55 mixin("Like!it as_"~it.stringof~";"); 56 // Only one alias this allowed by now 57 //mixin("alias as_"~IT[0].stringof~" this;"); 58 } 59 static if (IT.length == 1) 60 mixin("alias as_"~IT[0].stringof~" this;"); 61 } 62 63 static foreach (m; [__traits(derivedMembers, T)]) 64 static if (__traits(isVirtualFunction, __traits(getMember, T, m))) 65 static foreach (fn; __traits(getOverloads, T, m)) 66 mixin(buildDelegate!fn ~ buildCall!(fn, m)); 67 68 69 void fillDelegatesFrom(bool nullCheck=true, X)(ref X src) 70 { 71 alias dst = this; 72 73 static if (nullCheck && (is(X == interface) || is(X == class))) 74 { 75 version (D_BetterC) assert(src !is null, "object is null"); 76 else enforce(src !is null, "object is null"); 77 } 78 79 static string buildMakeOrAssign(alias fn, string m)() 80 { 81 enum dstdlg = "dst." ~ dlgName!fn; 82 enum srcm = "src." ~ m; 83 static if (hasFunctionAttributes!(fn, "@property") && 84 !isFunction!(mixin(srcm))) 85 { 86 enum retsrcm = "return " ~ srcm ~ ";"; 87 enum pref = refPref!fn; 88 static if (arity!fn == 1) 89 { 90 enum ret = !is(ReturnType!fn == void); 91 return dstdlg ~ " = "~pref~"(v) { "~srcm~" = v; "~(ret?retsrcm:"")~" };"; 92 } 93 else static if (arity!fn == 0) 94 return dstdlg ~ " = "~pref~" () " ~ fnAttr!(fn, ["@property"]) ~ " { "~retsrcm~" };"; 95 else 96 static assert(0, "property must have 0 or 1 parameter"); 97 } 98 else return dstdlg ~ " = &"~srcm~";"; 99 } 100 101 alias IT = InterfacesTuple!T; 102 static if (IT.length) 103 static foreach (it; IT) 104 mixin("this.as_"~it.stringof~".fillDelegatesFrom!nullCheck(src);"); 105 106 static foreach (m; [__traits(derivedMembers, T)]) 107 static if (__traits(isVirtualFunction, __traits(getMember, T, m))) 108 static foreach (fn; __traits(getOverloads, T, m)) 109 mixin(buildMakeOrAssign!(fn, m)); 110 } 111 } 112 113 /// 114 Like!T as(T, bool nullCheck=true, X)(ref X obj) 115 if (is(T == interface)) 116 { 117 Like!T ret; 118 ret.fillDelegatesFrom!nullCheck(obj); 119 return ret; 120 } 121 122 123 version (D_BetterC) { } 124 else 125 { 126 import std : format, enforce; 127 128 alias LikeObj(T, S) = LikeWrapper!(T, S, true); 129 130 alias LikeObjCtx(T, S) = LikeWrapper!(T, S, false); 131 132 /// 133 class LikeWrapper(T, S, bool isRef=false, string ctxName="__context_", bool isFinal=true) : T 134 if (is(T == interface)) 135 { 136 mixin((isFinal?"private":"protected")~" Unqual!S"~(isRef?"*":"")~" "~ctxName~";"); 137 138 static if (isRef) 139 { 140 this(ref Unqual!S c) { mixin(ctxName ~ " = &c;"); } 141 this(ref const Unqual!S c) const { mixin(ctxName ~ " = &c;"); } 142 } 143 else 144 { 145 this(S c) pure 146 { 147 static if (__traits(compiles, S.init is null)) 148 enforce(c !is null, "context is null"); 149 mixin(ctxName ~ " = c;"); 150 } 151 } 152 153 private static string buildCall(alias fn, string name)() 154 { 155 enum args = fn.mangleof ~ "_args"; 156 enum aref = refPref!fn; 157 158 string callOrFieldUse() 159 { 160 enum ctxfld = ctxName ~ "." ~ name; 161 static if (hasFunctionAttributes!(fn, "@property") && 162 !isFunction!(typeof(mixin(ctxfld)))) 163 { 164 enum retctxfld = "return " ~ ctxfld ~ ";"; 165 static if (arity!fn == 1) 166 { 167 enum ret = !is(ReturnType!fn == void); 168 return ctxfld~" = "~args~"[0]; "~(ret?retctxfld:""); 169 } 170 else static if (arity!fn == 0) return retctxfld; 171 else static assert(0, "property must have 0 or 1 parameter"); 172 } 173 else return "return " ~ ctxfld ~ "(" ~ args ~ ");"; 174 } 175 176 return "%7$s override %1$s %2$s ReturnType!fn %3$s(Parameters!fn %4$s) %5$s { %6$s }\n" 177 .format("", aref, name, args, fnAttr!fn, callOrFieldUse(), isFinal ? "final" : ""); 178 } 179 180 static foreach (m; [__traits(allMembers, T)]) 181 static if (__traits(isVirtualFunction, __traits(getMember, T, m))) 182 static foreach (fn; __traits(getOverloads, T, m)) 183 mixin(buildCall!(fn, m)); 184 } 185 186 /// 187 auto toObj(X)(auto ref X th) @property 188 if (is(Unqual!X == Like!T, T)) 189 { 190 static if (is(Unqual!X == Like!T, T)) 191 { 192 static if (is(ConstOf!X == X)) 193 return new const LikeObjCtx!(T, Like!T)(th); 194 else 195 return new LikeObjCtx!(T, Like!T)(th); 196 } 197 else static assert(0); 198 } 199 200 /// 201 auto asObj(T, bool nullCheck=true, X)(ref X obj) 202 if (is(T == interface)) 203 { 204 static if (is(ConstOf!X == X)) 205 return new const LikeObj!(T, X)(obj); 206 else 207 return new LikeObj!(T, X)(obj); 208 } 209 210 /// 211 auto asObjCtx(T, bool nullCheck=true, X)(auto ref X obj) 212 if (is(T == interface)) 213 { 214 static if (is(ConstOf!X == X)) 215 return new const LikeObjCtx!(T, X)(obj); 216 else 217 return new LikeObjCtx!(T, X)(obj); 218 } 219 } 220 221 version(unittest) 222 { 223 interface ConstFoo 224 { 225 @safe: 226 nothrow const: 227 int one(); 228 int one(int x); 229 int two(); 230 231 } 232 233 interface Foo : ConstFoo 234 { 235 @safe: 236 void okda(); 237 } 238 239 int useFoo(Like!Foo obj) @safe 240 { 241 obj.okda(); 242 return obj.one() + obj.two() + obj.one(100); 243 } 244 } 245 246 unittest 247 { 248 int k = 1; 249 250 struct Bar 251 { 252 int a, b; 253 @safe: 254 void okda() { k = 3; } 255 nothrow const: 256 int one() { return a * 2 + k; } 257 int one(int x) { return a * x; } 258 int two() { return a + b; } 259 } 260 261 auto bar = Bar(2, 4); 262 assert(useFoo(bar.as!Foo) == 213); 263 assert(k == 3); 264 } 265 266 unittest 267 { 268 static class Bar : Foo 269 { 270 void okda() {} 271 override const: 272 int one() { return 5; } 273 int one(int x) { return x * 2; } 274 int two() { return 3; } 275 } 276 277 auto bar = new Bar; 278 assert(useFoo(bar.as!Foo) == 208); 279 } 280 281 unittest 282 { 283 static interface Foo2 { int func() @nogc; } 284 static void test(Like!Foo2 obj) @nogc { assert(obj.func() == 42); } 285 286 static class BarC : Foo2 287 { override int func() @nogc { return 42; } } 288 289 auto barC = new BarC; 290 (() @nogc { test(barC.as!(Foo2, false)); })(); 291 292 static struct BarS 293 { int func() @nogc { return 42; } } 294 295 BarS barS; 296 (() @nogc { test(barS.as!Foo2); })(); 297 298 import std : assertThrown; 299 BarC nullBarC; 300 assertThrown( nullBarC.as!Foo2 ); 301 } 302 303 @safe 304 unittest 305 { 306 static interface Foo3 { int func() @safe; } 307 static void test(Like!Foo3 obj) @safe { assert(obj.func() == 42); } 308 309 static class Bar : Foo3 310 { override int func() @nogc { return 42; } } 311 312 auto bar = new Bar; 313 (() @safe { test(bar.as!Foo3); })(); 314 } 315 316 version(unittest) 317 { 318 int useFooObj(Foo obj) @safe 319 { 320 obj.okda(); 321 return obj.one() + obj.two() + obj.one(100); 322 } 323 324 int useConstFoo(const Like!ConstFoo obj) @safe 325 { return obj.one() + obj.two() + obj.one(1000); } 326 327 int useConstFooObj(const ConstFoo obj) @safe 328 { return obj.one() + obj.two() + obj.one(1000); } 329 } 330 331 unittest 332 { 333 int k = 1; 334 335 struct Bar 336 { 337 int a, b; 338 @safe: 339 void okda() { k = 3; } 340 nothrow const: 341 int one() { return a * 2 + k; } 342 int one(int x) { return a * x; } 343 int two() { return a + b; } 344 } 345 346 auto bar1 = Bar(2, 4); 347 assert(useFooObj(bar1.asObj!Foo) == 213); 348 assert(useFooObj(bar1.asObjCtx!Foo) == 213); 349 assert(k == 3); 350 auto b1 = bar1.as!Foo; 351 assert(useFooObj(b1.toObj) == 213); 352 353 assert(useConstFooObj(bar1.asObj!Foo) == 2013); 354 assert(useConstFooObj(bar1.asObjCtx!Foo) == 2013); 355 auto b2 = bar1.as!Foo; 356 assert(useConstFooObj(b2.toObj) == 2013); 357 358 const bar2 = Bar(1, 2); 359 assert(useConstFoo(bar2.as!ConstFoo) == 1008); 360 auto bb2 = bar2.as!ConstFoo; 361 assert(useConstFooObj(bb2.toObj) == 1008); 362 } 363 364 version (unittest) 365 { 366 void testField(I, S)() 367 { 368 auto s = S(12); 369 auto w = s.as!I; 370 371 enum setreturn = is(typeof(I.init.field = 12) == int); 372 373 assert (w.field == 12); 374 assert ((cast(const)w).field == 12); 375 static if (setreturn) assert ((w.field = 42) == 42); 376 else w.field = 42; 377 assert (w.field == 42); 378 assert ((cast(const)w).field == 42); 379 assert (s.field == 42); 380 381 auto o = w.toObj; 382 assert (o.field == 42); 383 static if (setreturn) assert ((o.field = 50) == 50); 384 else o.field = 50; 385 assert (w.field == 50); 386 assert (s.field == 50); 387 assert (o.field == 50); 388 389 auto c = s.asObjCtx!I; 390 assert (c.field == 50); 391 static if (setreturn) assert ((c.field = 123) == 123); 392 else c.field = 123; 393 assert (w.field == 50); 394 assert (s.field == 50); 395 assert (o.field == 50); 396 assert (c.field == 123); 397 } 398 } 399 400 unittest 401 { 402 static interface F1 403 { 404 @safe: 405 int field() const @property; 406 void field(int v) @property; 407 } 408 409 static interface F2 410 { 411 @safe: 412 ref const(int) field() const @property; 413 ref int field() @property; 414 void field(int v) @property; 415 } 416 417 static interface F3 418 { 419 @safe: 420 int field() const @property; 421 int field(int v) @property; 422 } 423 424 struct S1 { int field; } 425 426 static struct S2 427 { 428 int _field; 429 @safe: 430 int field() const @property { return _field; } 431 void field(int v) @property { _field = v; } 432 } 433 434 static struct S3 435 { 436 int _field; 437 @safe: 438 int field() const @property { return _field; } 439 int field(int v) @property { _field = v; return _field; } 440 } 441 442 testField!(F1, S1); 443 testField!(F2, S1); 444 testField!(F3, S1); 445 446 testField!(F1, S2); 447 //testField!(F2, S2); 448 //testField!(F3, S2); 449 450 //testField!(F1, S3); 451 //testField!(F2, S3); 452 testField!(F3, S3); 453 } 454 455 unittest 456 { 457 static interface PFoo 458 { 459 ref inout(int) field() inout @property; 460 } 461 462 struct SPFoo { int field; } 463 464 auto spfoo = SPFoo(12); 465 466 auto wrap = spfoo.asObjCtx!PFoo; 467 468 assert (wrap.field == 12); 469 wrap.field = 42; 470 assert (wrap.field == 42); 471 assert (spfoo.field == 12); 472 wrap.field = 32; 473 assert (wrap.field == 32); 474 assert (spfoo.field == 12); 475 }