1 module vibe.internal.interfaceproxy; 2 3 import vibe.internal.traits; 4 import vibe.internal.freelistref; 5 import vibe.internal.allocator; 6 import std.algorithm.mutation : move, swap; 7 import std.meta : staticMap; 8 import std.traits : BaseTypeTuple; 9 10 11 O asInterface(I, O)(O obj) if (is(I == interface) && is(O : I)) { return obj; } 12 InterfaceProxyClass!(I, O) asInterface(I, O)(O obj) if (is(I == interface) && !is(O : I)) { return new InterfaceProxyClass!(I, O)(obj); } 13 14 InterfaceProxyClass!(I, O) asInterface(I, O)(O obj, IAllocator allocator) 15 @trusted if (is(I == interface) && !is(O : I)) 16 { 17 alias R = InterfaceProxyClass!(I, O); 18 return allocator.makeGCSafe!R(obj); 19 } 20 21 void freeInterface(I, O)(InterfaceProxyClass!(I, O) inst, IAllocator allocator) 22 { 23 allocator.disposeGCSafe(inst); 24 } 25 26 FreeListRef!(InterfaceProxyClass!(I, O)) asInterfaceFL(I, O)(O obj) { return FreeListRef!(InterfaceProxyClass!(I, O))(obj); } 27 28 InterfaceProxy!I interfaceProxy(I, O)(O o) { return InterfaceProxy!I(o); } 29 30 private final class InterfaceProxyClass(I, O) : I 31 { 32 import std.meta : AliasSeq; 33 import std.traits : FunctionAttribute, MemberFunctionsTuple, ReturnType, ParameterTypeTuple, functionAttributes; 34 35 private { 36 O m_obj; 37 } 38 39 this(ref O obj) { swap(m_obj, obj); } 40 41 mixin methodDefs!0; 42 43 private mixin template methodDefs(size_t idx) { 44 alias Members = AliasSeq!(__traits(allMembers, I)); 45 static if (idx < Members.length) { 46 mixin overloadDefs!(Members[idx]); 47 mixin methodDefs!(idx+1); 48 } 49 } 50 51 mixin template overloadDefs(string mem) { 52 alias Overloads = MemberFunctionsTuple!(I, mem); 53 54 private static string impl() 55 { 56 import std.format : format; 57 string ret; 58 foreach (idx, F; Overloads) { 59 alias R = ReturnType!F; 60 enum attribs = functionAttributeString!F(false); 61 static if (__traits(isVirtualMethod, F)) { 62 static if (is(R == void)) 63 ret ~= q{override %s void %s(ParameterTypeTuple!(Overloads[%s]) params) { m_obj.%s(params); }} 64 .format(attribs, mem, idx, mem); 65 else 66 ret ~= q{override %s ReturnType!(Overloads[%s]) %s(ParameterTypeTuple!(Overloads[%s]) params) { return m_obj.%s(params); }} 67 .format(attribs, idx, mem, idx, mem); 68 } 69 } 70 return ret; 71 } 72 73 mixin(impl()); 74 } 75 } 76 77 78 79 struct InterfaceProxy(I) if (is(I == interface)) { 80 import std.meta : AliasSeq; 81 import std.traits : FunctionAttribute, MemberFunctionsTuple, ReturnType, ParameterTypeTuple, functionAttributes; 82 import vibe.internal.traits : checkInterfaceConformance; 83 84 private { 85 void*[4] m_value; 86 enum maxSize = m_value.length * m_value[0].sizeof; 87 Proxy m_intf; 88 } 89 90 this(IP : InterfaceProxy!J, J)(IP proxy) @safe 91 { 92 () @trusted { 93 m_intf = proxy.m_intf; 94 swap(proxy.m_value, m_value); 95 proxy.m_intf = null; 96 } (); 97 } 98 99 this(O)(O object) @trusted 100 { 101 static assert(O.sizeof % m_value[0].sizeof == 0, "Sizeof object ("~O.stringof~") must be a multiple of a pointer size."); 102 static assert(O.sizeof <= maxSize, "Object ("~O.stringof~") is too big to be stored in an InterfaceProxy."); 103 import std.conv : emplace; 104 m_intf = ProxyImpl!O.get(); 105 static if (is(O == struct)) 106 emplace!O(m_value[0 .. O.sizeof/m_value[0].sizeof]); 107 swap((cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0], object); 108 } 109 110 ~this() @safe 111 { 112 clear(); 113 } 114 115 this(this) @safe 116 { 117 if (m_intf) m_intf._postblit(m_value); 118 } 119 120 void clear() @safe nothrow 121 { 122 if (m_intf) { 123 m_intf._destroy(m_value); 124 m_intf = null; 125 m_value[] = null; 126 } 127 } 128 129 T extract(T)() 130 @trusted nothrow { 131 if (!m_intf || m_intf._typeInfo() !is typeid(T)) 132 assert(false, "Extraction of wrong type from InterfaceProxy."); 133 return (cast(T[])m_value[0 .. T.sizeof/m_value[0].sizeof])[0]; 134 } 135 136 void opAssign(IP : InterfaceProxy!J, J)(IP proxy) @safe 137 { 138 static assert(is(J : I), "Can only assign InterfaceProxy instances of derived interfaces."); 139 140 clear(); 141 if (proxy.m_intf) { 142 m_intf = proxy.m_intf; 143 m_value[] = proxy.m_value[]; 144 proxy.m_intf = null; 145 } 146 } 147 148 void opAssign(O)(O object) @trusted 149 if (checkInterfaceConformance!(O, I) is null) 150 { 151 static assert(O.sizeof % m_value[0].sizeof == 0, "Sizeof object ("~O.stringof~") must be a multiple of a pointer size."); 152 static assert(O.sizeof <= maxSize, "Object is too big to be stored in an InterfaceProxy."); 153 import std.conv : emplace; 154 clear(); 155 m_intf = ProxyImpl!O.get(); 156 if (is(O == class)) 157 (cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0] = object; 158 else emplace!O(m_value[0 .. O.sizeof/m_value[0].sizeof]); 159 swap((cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0], object); 160 } 161 162 bool opCast(T)() const @safe nothrow if (is(T == bool)) { return m_intf !is null; } 163 164 mixin allMethods!0; 165 166 private mixin template allMethods(size_t idx) { 167 alias Members = AliasSeq!(__traits(allMembers, I)); 168 static if (idx < Members.length) { 169 static if (__traits(compiles, __traits(getMember, I, Members[idx]))) 170 mixin overloadMethods!(Members[idx]); 171 mixin allMethods!(idx+1); 172 } 173 } 174 175 private mixin template overloadMethods(string member) { 176 alias Overloads = AliasSeq!(__traits(getOverloads, I, member)); 177 178 private static string impl() 179 { 180 import std.format : format; 181 string ret; 182 foreach (idx, F; Overloads) { 183 enum attribs = functionAttributeString!F(false); 184 enum is_prop = functionAttributes!F & FunctionAttribute.property; 185 ret ~= q{%s ReturnType!(Overloads[%s]) %s(%s) { return m_intf.%s(m_value, %s); }} 186 .format(attribs, idx, member, parameterDecls!(F, idx), member, parameterNames!F); 187 } 188 return ret; 189 } 190 191 mixin(impl()); 192 } 193 194 private interface Proxy : staticMap!(ProxyOf, BaseTypeTuple!I) { 195 import std.meta : AliasSeq; 196 import std.traits : FunctionAttribute, MemberFunctionsTuple, ReturnType, ParameterTypeTuple, functionAttributes; 197 198 void _destroy(void[] stor) @safe nothrow; 199 void _postblit(void[] stor) @safe nothrow; 200 TypeInfo _typeInfo() @safe nothrow; 201 202 mixin methodDecls!0; 203 204 private mixin template methodDecls(size_t idx) { 205 alias Members = AliasSeq!(__traits(derivedMembers, I)); 206 static if (idx < Members.length) { 207 static if (__traits(compiles, __traits(getMember, I, Members[idx]))) 208 mixin overloadDecls!(Members[idx]); 209 mixin methodDecls!(idx+1); 210 } 211 } 212 213 private mixin template overloadDecls(string mem) { 214 alias Overloads = AliasSeq!(__traits(getOverloads, I, mem)); 215 216 private static string impl() 217 { 218 import std.format : format; 219 string ret; 220 foreach (idx, F; Overloads) { 221 enum attribs = functionAttributeString!F(false); 222 enum vtype = functionAttributeThisType!F("void[]"); 223 ret ~= q{ReturnType!(Overloads[%s]) %s(%s obj, %s) %s;} 224 .format(idx, mem, vtype, parameterDecls!(F, idx), attribs); 225 } 226 return ret; 227 } 228 229 mixin(impl()); 230 } 231 } 232 233 static final class ProxyImpl(O) : Proxy { 234 static auto get() 235 { 236 static ProxyImpl impl; 237 if (!impl) impl = new ProxyImpl; 238 return impl; 239 } 240 241 override void _destroy(void[] stor) 242 @trusted nothrow { 243 static if (is(O == struct)) { 244 try destroy(_extract(stor)); 245 catch (Exception e) assert(false, "Destructor has thrown: "~e.msg); 246 } 247 } 248 249 override void _postblit(void[] stor) 250 @trusted nothrow { 251 static if (is(O == struct)) { 252 try typeid(O).postblit(stor.ptr); 253 catch (Exception e) assert(false, "Postblit contructor has thrown: "~e.msg); 254 } 255 } 256 257 override TypeInfo _typeInfo() 258 @safe nothrow { 259 return typeid(O); 260 } 261 262 static ref inout(O) _extract(inout(void)[] stor) 263 @trusted nothrow pure @nogc { 264 if (stor.length < O.sizeof) assert(false); 265 return *cast(inout(O)*)stor.ptr; 266 } 267 268 mixin methodDefs!0; 269 270 private mixin template methodDefs(size_t idx) { 271 alias Members = AliasSeq!(__traits(allMembers, I)); 272 static if (idx < Members.length) { 273 static if (__traits(compiles, __traits(getMember, I, Members[idx]))) 274 mixin overloadDefs!(Members[idx]); 275 mixin methodDefs!(idx+1); 276 } 277 } 278 279 private mixin template overloadDefs(string mem) { 280 alias Overloads = AliasSeq!(__traits(getOverloads, I, mem)); 281 282 private static string impl() 283 { 284 import std.format : format; 285 string ret; 286 foreach (idx, F; Overloads) { 287 alias R = ReturnType!F; 288 alias P = ParameterTypeTuple!F; 289 enum attribs = functionAttributeString!F(false); 290 enum vtype = functionAttributeThisType!F("void[]"); 291 292 static if (is(R == void)) 293 ret ~= q{override void %s(%s obj, %s) %s { _extract(obj).%s(%s); }} 294 .format(mem, vtype, parameterDecls!(F, idx), attribs, mem, parameterNames!F); 295 else 296 ret ~= q{override ReturnType!(Overloads[%s]) %s(%s obj, %s) %s { return _extract(obj).%s(%s); }} 297 .format(idx, mem, vtype, parameterDecls!(F, idx), attribs, mem, parameterNames!F); 298 } 299 return ret; 300 } 301 302 mixin(impl()); 303 } 304 } 305 } 306 307 private string parameterDecls(alias F, size_t idx)() 308 { 309 import std.format : format; 310 import std.traits : ParameterTypeTuple, ParameterStorageClass, ParameterStorageClassTuple; 311 312 string ret; 313 alias PST = ParameterStorageClassTuple!F; 314 foreach (i, PT; ParameterTypeTuple!F) { 315 static if (i > 0) ret ~= ", "; 316 static if (PST[i] & ParameterStorageClass.scope_) ret ~= "scope "; 317 static if (PST[i] & ParameterStorageClass.out_) ret ~= "out "; 318 static if (PST[i] & ParameterStorageClass.ref_) ret ~= "ref "; 319 static if (PST[i] & ParameterStorageClass.lazy_) ret ~= "lazy "; 320 ret ~= format("ParameterTypeTuple!(Overloads[%s])[%s] param_%s", idx, i, i); 321 } 322 return ret; 323 } 324 325 private string parameterNames(alias F)() 326 { 327 import std.format : format; 328 import std.traits : ParameterTypeTuple; 329 330 string ret; 331 foreach (i, PT; ParameterTypeTuple!F) { 332 static if (i > 0) ret ~= ", "; 333 ret ~= format("param_%s", i); 334 } 335 return ret; 336 } 337 338 private alias ProxyOf(I) = InterfaceProxy!I.Proxy; 339 340 341 unittest { 342 static interface I { 343 @property int count() const; 344 } 345 346 static struct S { 347 int* cnt; 348 this(bool) { cnt = new int; *cnt = 1; } 349 this(this) { if (cnt) (*cnt)++; } 350 ~this() { if (cnt) (*cnt)--; } 351 @property int count() const { return cnt ? *cnt : 0; } 352 } 353 354 auto s = S(true); 355 assert(s.count == 1); 356 357 auto t = interfaceProxy!I(s); 358 assert(s.count == 2); 359 360 t = interfaceProxy!I(s); 361 assert(s.count == 2); 362 363 t = s; 364 assert(s.count == 2); 365 366 s = S.init; 367 assert(t.count == 1); 368 369 s = t.extract!S; 370 assert(s.count == 2); 371 372 t = InterfaceProxy!I.init; 373 assert(s.count == 1); 374 375 t = s; 376 assert(s.count == 2); 377 378 s = S(true); 379 assert(s.count == 1); 380 assert(t.count == 1); 381 382 { 383 InterfaceProxy!I u; 384 u = s; 385 assert(u.count == 2); 386 } 387 assert(s.count == 1); 388 }