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)) {}