1 module concepts.implements;
2 
3 import std.traits : isAbstractClass, isAggregateType;
4 
5 alias Identity(alias T) = T;
6 private enum isPrivate(T, string member) = !__traits(compiles, __traits(getMember, T, member));
7 
8 
9 template implements(alias T, alias Interface)
10     if (isAggregateType!T && isAbstractClass!Interface) {
11 
12     static if(__traits(compiles, check())) {
13         bool implements() {
14             return true;
15         }
16     } else {
17         bool implements() {
18             // force compilation error
19             check();
20             return false;
21         }
22     }
23 
24     private void check() @safe pure {
25         enum mixinStr = `auto _ = ` ~ implInterfaceStr ~ `;`;
26         //pragma(msg, mixinStr);
27         mixin(mixinStr);
28     }
29 
30     private string implInterfaceStr() {
31         string implString = "new class Interface {\n";
32         foreach(memberName; __traits(allMembers, Interface)) {
33 
34             static if(!isPrivate!(Interface, memberName)) {
35 
36                 alias member = Identity!(__traits(getMember, Interface, memberName));
37 
38                 static if(__traits(isAbstractFunction, member)) {
39                     foreach(overload; __traits(getOverloads, Interface, memberName)) {
40                         foreach(line; implMethodStr!overload) {
41                             implString ~= `    ` ~ line ~ "\n";
42                         }
43                     }
44                 }
45             }
46 
47         }
48 
49         implString ~= "}";
50 
51         return implString;
52     }
53 
54     // returns a string to mixin that implements the method
55     private string[] implMethodStr(alias method)() {
56 
57         import std.traits: ReturnType, Parameters, moduleName;
58         import std.meta: staticMap;
59         import std.algorithm: map;
60         import std.range: iota;
61         import std.array: join, array;
62         import std.conv: text;
63 
64 
65         if(!__ctfe) return [];
66 
67         // e.g. int arg0, string arg1
68         string typeArgs() {
69             string[] args;
70             foreach(i, _; Parameters!method) {
71                 args ~= text(Parameters!method[i].stringof, ` arg`, i);
72             }
73             return args.join(", ");
74         }
75 
76         // e.g. arg0, arg1
77         string callArgs() {
78             return Parameters!method.length.iota.map!(i => text(`arg`, i)).array.join(`, `);
79         }
80 
81         string[] importMixin(T)() {
82             static if(__traits(compiles, moduleName!T)) {
83                 return [`import ` ~ moduleName!T ~ `: ` ~ T.stringof ~ `;`];
84             } else
85                 return [];
86         }
87 
88         string[] ret;
89 
90         ret ~= importMixin!(ReturnType!method);
91 
92         foreach(P; Parameters!method) {
93             ret ~= importMixin!P;
94         }
95 
96         enum methodName = __traits(identifier, method);
97 
98         ret ~=
99             `override ` ~ ReturnType!method.stringof ~ " " ~ methodName ~
100             `(` ~ typeArgs ~ `) {` ~
101             ` return T` ~ `.init.` ~ methodName ~ `(` ~ callArgs() ~ `);` ~
102             ` }`;
103 
104         return ret;
105     }
106 
107 }
108 
109 
110 @("Foo implements IFoo")
111 @safe unittest {
112     static assert(__traits(compiles, implements!(Foo, IFoo)));
113     static assert(!__traits(compiles, implements!(Bar, IFoo)));
114     static assert(!__traits(compiles, implements!(Foo, IBar)));
115     static assert(__traits(compiles, implements!(Bar, IBar)));
116     static assert(!__traits(compiles, implements!(UnsafeBar, IBar)));
117 
118     static assert(__traits(compiles, useFoo(Foo())));
119     static assert(!__traits(compiles, useBar(Foo())));
120     static assert(!__traits(compiles, useFoo(Bar())));
121     static assert(__traits(compiles, useBar(Bar())));
122 }
123 
124 @("FooBar implements IFoo and IBar")
125 @safe unittest {
126     static assert(__traits(compiles, implements!(FooBar, IFoo)));
127     static assert(__traits(compiles, implements!(FooBar, IBar)));
128 
129     static assert(__traits(compiles, useFoo(FooBar())));
130     static assert(__traits(compiles, useBar(FooBar())));
131 
132     static assert(__traits(compiles, useFooandBar(FooBar())));
133 }
134 
135 @("FooClass implements IFoo")
136 @safe unittest {
137     static assert(__traits(compiles, implements!(FooClass, IFoo)));
138     static assert(__traits(compiles, useFoo(FooClass.init)));
139 }
140 
141 @("Foo implements FooAbstractClass")
142 @safe unittest {
143     static assert(__traits(compiles, implements!(Foo, FooAbstractClass)));
144 }
145 
146 version(unittest):
147 
148 interface IFoo {
149     int foo(int i, string s) @safe;
150     double lefoo(string s) @safe;
151 }
152 
153 interface IBar {
154     string bar(double d) @safe;
155     void bar(string s) @safe;
156 }
157 
158 struct Foo {
159     int foo(int i, string s) @safe { return 0; }
160     double lefoo(string s) @safe { return 0; }
161 }
162 
163 struct Bar {
164     string bar(double d) @safe { return ""; }
165     void bar(string s) @safe { }
166 }
167 
168 struct UnsafeBar {
169     string bar(double d) @system { return ""; }
170     void bar(string s) @system { }
171 }
172 
173 struct FooBar {
174     int foo(int i, string s) @safe { return 0; }
175     double lefoo(string s) @safe { return 0; }
176     string bar(double d) @safe { return ""; }
177     void bar(string s) @safe { }
178 }
179 
180 class FooClass {
181     int foo(int i, string s) @safe { return 0; }
182     double lefoo(string s) @safe { return 0; }
183 }
184 
185 class FooAbstractClass {
186     abstract int foo(int i, string s) @safe;
187     final double lefoo(string s) @safe { return 0; }
188 }
189 
190 void useFoo(T)(T) if(implements!(T, IFoo)) {}
191 void useBar(T)(T) if(implements!(T, IBar)) {}
192 void useFooandBar(T)(T) if(implements!(T, IFoo) && implements!(T, IBar)) {}