1 ///
2 module aslike;
3 
4 import std.traits;
5 import std.string : join, format;
6 import std.exception : enforce;
7 
8 ///
9 struct Like(T) if (is(T == interface))
10 {
11     private template dlgName(alias fn) { enum dlgName = "__dlg_" ~ fn.mangleof; }
12     private template fnAttr(alias fn)
13     { enum fnAttr = [__traits(getFunctionAttributes, fn)].join(" "); }
14 
15     static foreach (m; [__traits(allMembers, T)])
16         static if (__traits(isVirtualFunction, __traits(getMember, T, m)))
17             static foreach (fn; __traits(getOverloads, T, m))
18                 mixin(format!("private %1$s delegate(%2$s) %3$s %4$s;\n" ~
19                               "%1$s %5$s (%2$s args) %3$s { return %4$s(args); }")
20                               ("ReturnType!fn", "Parameters!fn", fnAttr!fn, dlgName!fn, m));
21 }
22 
23 ///
24 Like!T as(T, bool nullCheck=true, X)(auto ref X obj) if (is(T == interface))
25 {
26     Like!T ret;
27 
28     static if (nullCheck && (is(X == interface) || is(X == class)))
29         enforce(obj !is null, "object is null");
30     
31     static foreach (m; [__traits(allMembers, T)])
32         static if (__traits(isVirtualFunction, __traits(getMember, T, m)))
33             static foreach (fn; __traits(getOverloads, T, m))
34                 mixin("ret." ~ ret.dlgName!fn ~ " = &obj."~m~";");
35 
36     return ret;
37 }
38 
39 version(unittest)
40 {
41     interface Foo
42     {
43     @safe:
44         void okda();
45     nothrow const:
46         int one();
47         int one(int x);
48         int two();
49     }
50 
51     static int useFoo(Like!Foo obj) @safe
52     {
53         obj.okda();
54         return obj.one() + obj.two() + obj.one(100);
55     }
56 }
57 
58 unittest
59 {
60     int k = 1;
61 
62     struct Bar
63     {
64         int a, b;
65     @safe:
66         void okda() { k = 3; }
67     nothrow const:
68         int one() { return a * 2 + k; }
69         int one(int x) { return a * x; }
70         int two() { return a + b; }
71     }
72 
73     assert(useFoo(Bar(2, 4).as!Foo) == 213);
74 }
75 
76 unittest
77 {
78     static class Bar : Foo
79     {
80         void okda() {}
81     override const:
82         int one() { return 5; }
83         int one(int x) { return x * 2; }
84         int two() { return 3; }
85     }
86 
87     assert(useFoo((new Bar).as!Foo) == 208);
88 }
89 
90 unittest
91 {
92     static interface Foo2 { int func() @nogc; }
93     static void test(Like!Foo2 obj) @nogc { assert(obj.func() == 42); }
94 
95     static class BarC : Foo2
96     { override int func() @nogc { return 42; } }
97 
98     auto barC = new BarC;
99     (() @nogc { test(barC.as!(Foo2, false)); })();
100 
101     static struct BarS
102     { int func() @nogc { return 42; } }
103 
104     BarS barS;
105     (() @nogc { test(barS.as!Foo2); })();
106 
107     import std : assertThrown;
108     BarC nullBarC;
109     assertThrown( nullBarC.as!Foo2 );
110 }
111 
112 @safe
113 unittest
114 {
115     static interface Foo3 { int func() @safe; }
116     static void test(Like!Foo3 obj) @safe { assert(obj.func() == 42); }
117 
118     static class Bar : Foo3
119     { override int func() @nogc { return 42; } }
120 
121     auto bar = new Bar;
122     (() @safe { test(bar.as!Foo3); })();
123 }