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 if (is(O == struct) || is(O == class) || is(O == interface)) 101 { 102 static assert(O.sizeof % m_value[0].sizeof == 0, "Sizeof object ("~O.stringof~") must be a multiple of a pointer size."); 103 static assert(O.sizeof <= maxSize, "Object ("~O.stringof~") is too big to be stored in an InterfaceProxy."); 104 import std.conv : emplace; 105 106 static if (is(O == class) || is(O == interface)) { 107 if (!object) return; 108 } 109 110 m_intf = ProxyImpl!O.get(); 111 static if (is(O == struct)) 112 emplace!O(m_value[0 .. O.sizeof/m_value[0].sizeof]); 113 swap((cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0], object); 114 } 115 116 this(typeof(null)) 117 { 118 } 119 120 ~this() @safe 121 { 122 clear(); 123 } 124 125 this(this) @safe 126 { 127 if (m_intf) m_intf._postblit(m_value); 128 } 129 130 void clear() @safe nothrow 131 { 132 if (m_intf) { 133 m_intf._destroy(m_value); 134 m_intf = null; 135 m_value[] = null; 136 } 137 } 138 139 T extract(T)() 140 @trusted nothrow { 141 if (!m_intf || m_intf._typeInfo() !is typeid(T)) 142 assert(false, "Extraction of wrong type from InterfaceProxy."); 143 return (cast(T[])m_value[0 .. T.sizeof/m_value[0].sizeof])[0]; 144 } 145 146 void opAssign(IP : InterfaceProxy!J, J)(IP proxy) @safe 147 { 148 static assert(is(J : I), "Can only assign InterfaceProxy instances of derived interfaces."); 149 150 clear(); 151 if (proxy.m_intf) { 152 m_intf = proxy.m_intf; 153 m_value[] = proxy.m_value[]; 154 proxy.m_intf = null; 155 } 156 } 157 158 void opAssign(O)(O object) @trusted 159 if (checkInterfaceConformance!(O, I) is null) 160 { 161 static assert(O.sizeof % m_value[0].sizeof == 0, "Sizeof object ("~O.stringof~") must be a multiple of a pointer size."); 162 static assert(O.sizeof <= maxSize, "Object is too big to be stored in an InterfaceProxy."); 163 import std.conv : emplace; 164 clear(); 165 m_intf = ProxyImpl!O.get(); 166 static if (is(O == class)) 167 (cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0] = object; 168 else emplace!O(m_value[0 .. O.sizeof/m_value[0].sizeof]); 169 swap((cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0], object); 170 } 171 172 bool opCast(T)() const @safe nothrow if (is(T == bool)) { return m_intf !is null; } 173 174 mixin allMethods!0; 175 176 private mixin template allMethods(size_t idx) { 177 alias Members = AliasSeq!(__traits(allMembers, I)); 178 static if (idx < Members.length) { 179 static if (__traits(compiles, __traits(getMember, I, Members[idx]))) 180 mixin overloadMethods!(Members[idx]); 181 mixin allMethods!(idx+1); 182 } 183 } 184 185 private mixin template overloadMethods(string member) { 186 alias Overloads = AliasSeq!(__traits(getOverloads, I, member)); 187 188 private static string impl() 189 { 190 import std.format : format; 191 string ret; 192 foreach (idx, F; Overloads) { 193 enum attribs = functionAttributeString!F(false); 194 enum is_prop = functionAttributes!F & FunctionAttribute.property; 195 ret ~= q{%s ReturnType!(Overloads[%s]) %s(%s) { return m_intf.%s(m_value, %s); }} 196 .format(attribs, idx, member, parameterDecls!(F, idx), member, parameterNames!F); 197 } 198 return ret; 199 } 200 201 mixin(impl()); 202 } 203 204 private interface Proxy : staticMap!(ProxyOf, BaseTypeTuple!I) { 205 import std.meta : AliasSeq; 206 import std.traits : FunctionAttribute, MemberFunctionsTuple, ReturnType, ParameterTypeTuple, functionAttributes; 207 208 void _destroy(void[] stor) @safe nothrow; 209 void _postblit(void[] stor) @safe nothrow; 210 TypeInfo _typeInfo() @safe nothrow; 211 212 mixin methodDecls!0; 213 214 private mixin template methodDecls(size_t idx) { 215 alias Members = AliasSeq!(__traits(derivedMembers, I)); 216 static if (idx < Members.length) { 217 static if (__traits(compiles, __traits(getMember, I, Members[idx]))) 218 mixin overloadDecls!(Members[idx]); 219 mixin methodDecls!(idx+1); 220 } 221 } 222 223 private mixin template overloadDecls(string mem) { 224 alias Overloads = AliasSeq!(__traits(getOverloads, I, mem)); 225 226 private static string impl() 227 { 228 import std.format : format; 229 string ret; 230 foreach (idx, F; Overloads) { 231 enum attribs = functionAttributeString!F(false); 232 enum vtype = functionAttributeThisType!F("void[]"); 233 ret ~= q{ReturnType!(Overloads[%s]) %s(%s obj, %s) %s;} 234 .format(idx, mem, vtype, parameterDecls!(F, idx), attribs); 235 } 236 return ret; 237 } 238 239 mixin(impl()); 240 } 241 } 242 243 static final class ProxyImpl(O) : Proxy { 244 static auto get() 245 { 246 static ProxyImpl impl; 247 if (!impl) impl = new ProxyImpl; 248 return impl; 249 } 250 251 override void _destroy(void[] stor) 252 @trusted nothrow { 253 static if (is(O == struct)) { 254 try destroy(_extract(stor)); 255 catch (Exception e) assert(false, "Destructor has thrown: "~e.msg); 256 } 257 } 258 259 override void _postblit(void[] stor) 260 @trusted nothrow { 261 static if (is(O == struct)) { 262 try typeid(O).postblit(stor.ptr); 263 catch (Exception e) assert(false, "Postblit contructor has thrown: "~e.msg); 264 } 265 } 266 267 override TypeInfo _typeInfo() 268 @safe nothrow { 269 return typeid(O); 270 } 271 272 static ref inout(O) _extract(inout(void)[] stor) 273 @trusted nothrow pure @nogc { 274 if (stor.length < O.sizeof) assert(false); 275 return *cast(inout(O)*)stor.ptr; 276 } 277 278 mixin methodDefs!0; 279 280 private mixin template methodDefs(size_t idx) { 281 alias Members = AliasSeq!(__traits(allMembers, I)); 282 static if (idx < Members.length) { 283 static if (__traits(compiles, __traits(getMember, I, Members[idx]))) 284 mixin overloadDefs!(Members[idx]); 285 mixin methodDefs!(idx+1); 286 } 287 } 288 289 private mixin template overloadDefs(string mem) { 290 alias Overloads = AliasSeq!(__traits(getOverloads, I, mem)); 291 292 private static string impl() 293 { 294 import std.format : format; 295 string ret; 296 foreach (idx, F; Overloads) { 297 alias R = ReturnType!F; 298 alias P = ParameterTypeTuple!F; 299 enum attribs = functionAttributeString!F(false); 300 enum vtype = functionAttributeThisType!F("void[]"); 301 302 static if (is(R == void)) 303 ret ~= q{override void %s(%s obj, %s) %s { _extract(obj).%s(%s); }} 304 .format(mem, vtype, parameterDecls!(F, idx), attribs, mem, parameterNames!F); 305 else 306 ret ~= q{override ReturnType!(Overloads[%s]) %s(%s obj, %s) %s { return _extract(obj).%s(%s); }} 307 .format(idx, mem, vtype, parameterDecls!(F, idx), attribs, mem, parameterNames!F); 308 } 309 return ret; 310 } 311 312 mixin(impl()); 313 } 314 } 315 } 316 317 unittest { 318 static interface I {} 319 assert(!InterfaceProxy!I(null)); 320 assert(!InterfaceProxy!I(cast(I)null)); 321 } 322 323 private string parameterDecls(alias F, size_t idx)() 324 { 325 import std.format : format; 326 import std.traits : ParameterTypeTuple, ParameterStorageClass, ParameterStorageClassTuple; 327 328 string ret; 329 alias PST = ParameterStorageClassTuple!F; 330 foreach (i, PT; ParameterTypeTuple!F) { 331 static if (i > 0) ret ~= ", "; 332 static if (PST[i] & ParameterStorageClass.scope_) ret ~= "scope "; 333 static if (PST[i] & ParameterStorageClass.out_) ret ~= "out "; 334 static if (PST[i] & ParameterStorageClass.ref_) ret ~= "ref "; 335 static if (PST[i] & ParameterStorageClass.lazy_) ret ~= "lazy "; 336 ret ~= format("ParameterTypeTuple!(Overloads[%s])[%s] param_%s", idx, i, i); 337 } 338 return ret; 339 } 340 341 private string parameterNames(alias F)() 342 { 343 import std.format : format; 344 import std.traits : ParameterTypeTuple; 345 346 string ret; 347 foreach (i, PT; ParameterTypeTuple!F) { 348 static if (i > 0) ret ~= ", "; 349 ret ~= format("param_%s", i); 350 } 351 return ret; 352 } 353 354 private alias ProxyOf(I) = InterfaceProxy!I.Proxy; 355 356 357 unittest { 358 static interface I { 359 @property int count() const; 360 } 361 362 static struct S { 363 int* cnt; 364 this(bool) { cnt = new int; *cnt = 1; } 365 this(this) { if (cnt) (*cnt)++; } 366 ~this() { if (cnt) (*cnt)--; } 367 @property int count() const { return cnt ? *cnt : 0; } 368 } 369 370 auto s = S(true); 371 assert(s.count == 1); 372 373 auto t = interfaceProxy!I(s); 374 assert(s.count == 2); 375 376 t = interfaceProxy!I(s); 377 assert(s.count == 2); 378 379 t = s; 380 assert(s.count == 2); 381 382 s = S.init; 383 assert(t.count == 1); 384 385 s = t.extract!S; 386 assert(s.count == 2); 387 388 t = InterfaceProxy!I.init; 389 assert(s.count == 1); 390 391 t = s; 392 assert(s.count == 2); 393 394 s = S(true); 395 assert(s.count == 1); 396 assert(t.count == 1); 397 398 { 399 InterfaceProxy!I u; 400 u = s; 401 assert(u.count == 2); 402 } 403 assert(s.count == 1); 404 }