1 module concepts.implements;
2 
3 import std.traits : isAbstractClass, isAggregateType;
4 
5 private 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 @("Foo implements IFoo")
110 @safe pure unittest {
111     static assert(__traits(compiles, implements!(Foo, IFoo)));
112     static assert(is(typeof({ implements!(Foo, IFoo); })));
113     static assert(!is(typeof({ implements!(Bar, IFoo); })));
114     static assert(!__traits(compiles, implements!(Bar, IFoo)));
115     static assert(!is(typeof({ implements!(Foo, IBar); })));
116 
117     static assert( is(typeof({ implements!(Bar,      IBar); })));
118     static assert(!is(typeof({ implements!(UnsafeBar, IBar); })));
119 
120     static assert(__traits(compiles, useFoo(Foo())));
121     static assert(!__traits(compiles, useBar(Foo())));
122     static assert(!__traits(compiles, useFoo(Bar())));
123     static assert(__traits(compiles, useBar(Bar())));
124 }
125 
126 @("FooBar implements IFoo and IBar")
127 @safe pure unittest {
128     static assert(__traits(compiles, implements!(FooBar, IFoo)));
129     static assert(__traits(compiles, implements!(FooBar, IBar)));
130 
131     static assert(__traits(compiles, useFoo(FooBar())));
132     static assert(__traits(compiles, useBar(FooBar())));
133 
134     static assert(__traits(compiles, useFooandBar(FooBar())));
135 }
136 
137 @("FooClass implements IFoo")
138 @safe pure unittest {
139     static assert(__traits(compiles, implements!(FooClass, IFoo)));
140     static assert(__traits(compiles, useFoo(FooClass.init)));
141 }
142 
143 @("Foo implements FooAbstractClass")
144 @safe pure unittest {
145     static assert(__traits(compiles, implements!(Foo, FooAbstractClass)));
146 }
147 
148 version(unittest):
149 
150 private interface IFoo {
151     int foo(int i, string s) @safe;
152     double lefoo(string s) @safe;
153 }
154 
155 private interface IBar {
156     string bar(double d) @safe;
157     void bar(string s) @safe;
158 }
159 
160 private struct Foo {
161     int foo(int i, string s) @safe { return 0; }
162     double lefoo(string s) @safe { return 0; }
163 }
164 
165 private struct Bar {
166     string bar(double d) @safe { return ""; }
167     void bar(string s) @safe { }
168 }
169 
170 private struct UnsafeBar {
171     string bar(double d) @system { return ""; }
172     void bar(string s) @system { }
173 }
174 
175 private struct FooBar {
176     int foo(int i, string s) @safe { return 0; }
177     double lefoo(string s) @safe { return 0; }
178     string bar(double d) @safe { return ""; }
179     void bar(string s) @safe { }
180 }
181 
182 private class FooClass {
183     int foo(int i, string s) @safe { return 0; }
184     double lefoo(string s) @safe { return 0; }
185 }
186 
187 private class FooAbstractClass {
188     abstract int foo(int i, string s) @safe;
189     final double lefoo(string s) @safe { return 0; }
190 }
191 
192 private void useFoo(T)(T) if(implements!(T, IFoo)) {}
193 private void useBar(T)(T) if(implements!(T, IBar)) {}
194 private void useFooandBar(T)(T) if(implements!(T, IFoo) && implements!(T, IBar)) {}