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