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