vibe-core/source/vibe/internal/interfaceproxy.d

389 lines
10 KiB
D

module vibe.internal.interfaceproxy;
import vibe.internal.traits;
import vibe.internal.freelistref;
import vibe.internal.allocator;
import std.algorithm.mutation : move, swap;
import std.meta : staticMap;
import std.traits : BaseTypeTuple;
O asInterface(I, O)(O obj) if (is(I == interface) && is(O : I)) { return obj; }
InterfaceProxyClass!(I, O) asInterface(I, O)(O obj) if (is(I == interface) && !is(O : I)) { return new InterfaceProxyClass!(I, O)(obj); }
InterfaceProxyClass!(I, O) asInterface(I, O)(O obj, IAllocator allocator)
@trusted if (is(I == interface) && !is(O : I))
{
alias R = InterfaceProxyClass!(I, O);
return allocator.makeGCSafe!R(obj);
}
void freeInterface(I, O)(InterfaceProxyClass!(I, O) inst, IAllocator allocator)
{
allocator.disposeGCSafe(inst);
}
FreeListRef!(InterfaceProxyClass!(I, O)) asInterfaceFL(I, O)(O obj) { return FreeListRef!(InterfaceProxyClass!(I, O))(obj); }
InterfaceProxy!I interfaceProxy(I, O)(O o) { return InterfaceProxy!I(o); }
private final class InterfaceProxyClass(I, O) : I
{
import std.meta : AliasSeq;
import std.traits : FunctionAttribute, MemberFunctionsTuple, ReturnType, ParameterTypeTuple, functionAttributes;
private {
O m_obj;
}
this(ref O obj) { swap(m_obj, obj); }
mixin methodDefs!0;
private mixin template methodDefs(size_t idx) {
alias Members = AliasSeq!(__traits(allMembers, I));
static if (idx < Members.length) {
mixin overloadDefs!(Members[idx]);
mixin methodDefs!(idx+1);
}
}
mixin template overloadDefs(string mem) {
alias Overloads = MemberFunctionsTuple!(I, mem);
private static string impl()
{
import std.format : format;
string ret;
foreach (idx, F; Overloads) {
alias R = ReturnType!F;
enum attribs = functionAttributeString!F(false);
static if (__traits(isVirtualMethod, F)) {
static if (is(R == void))
ret ~= q{override %s void %s(ParameterTypeTuple!(Overloads[%s]) params) { m_obj.%s(params); }}
.format(attribs, mem, idx, mem);
else
ret ~= q{override %s ReturnType!(Overloads[%s]) %s(ParameterTypeTuple!(Overloads[%s]) params) { return m_obj.%s(params); }}
.format(attribs, idx, mem, idx, mem);
}
}
return ret;
}
mixin(impl());
}
}
struct InterfaceProxy(I) if (is(I == interface)) {
import std.meta : AliasSeq;
import std.traits : FunctionAttribute, MemberFunctionsTuple, ReturnType, ParameterTypeTuple, functionAttributes;
import vibe.internal.traits : checkInterfaceConformance;
private {
void*[4] m_value;
enum maxSize = m_value.length * m_value[0].sizeof;
Proxy m_intf;
}
this(IP : InterfaceProxy!J, J)(IP proxy) @safe
{
() @trusted {
m_intf = proxy.m_intf;
swap(proxy.m_value, m_value);
proxy.m_intf = null;
} ();
}
this(O)(O object) @trusted
{
static assert(O.sizeof % m_value[0].sizeof == 0, "Sizeof object ("~O.stringof~") must be a multiple of a pointer size.");
static assert(O.sizeof <= maxSize, "Object ("~O.stringof~") is too big to be stored in an InterfaceProxy.");
import std.conv : emplace;
m_intf = ProxyImpl!O.get();
static if (is(O == struct))
emplace!O(m_value[0 .. O.sizeof/m_value[0].sizeof]);
swap((cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0], object);
}
~this() @safe
{
clear();
}
this(this) @safe
{
if (m_intf) m_intf._postblit(m_value);
}
void clear() @safe nothrow
{
if (m_intf) {
m_intf._destroy(m_value);
m_intf = null;
m_value[] = null;
}
}
T extract(T)()
@trusted nothrow {
if (!m_intf || m_intf._typeInfo() !is typeid(T))
assert(false, "Extraction of wrong type from InterfaceProxy.");
return (cast(T[])m_value[0 .. T.sizeof/m_value[0].sizeof])[0];
}
void opAssign(IP : InterfaceProxy!J, J)(IP proxy) @safe
{
static assert(is(J : I), "Can only assign InterfaceProxy instances of derived interfaces.");
clear();
if (proxy.m_intf) {
m_intf = proxy.m_intf;
m_value[] = proxy.m_value[];
proxy.m_intf = null;
}
}
void opAssign(O)(O object) @trusted
if (checkInterfaceConformance!(O, I) is null)
{
static assert(O.sizeof % m_value[0].sizeof == 0, "Sizeof object ("~O.stringof~") must be a multiple of a pointer size.");
static assert(O.sizeof <= maxSize, "Object is too big to be stored in an InterfaceProxy.");
import std.conv : emplace;
clear();
m_intf = ProxyImpl!O.get();
if (is(O == class))
(cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0] = object;
else emplace!O(m_value[0 .. O.sizeof/m_value[0].sizeof]);
swap((cast(O[])m_value[0 .. O.sizeof/m_value[0].sizeof])[0], object);
}
bool opCast(T)() const @safe nothrow if (is(T == bool)) { return m_intf !is null; }
mixin allMethods!0;
private mixin template allMethods(size_t idx) {
alias Members = AliasSeq!(__traits(allMembers, I));
static if (idx < Members.length) {
static if (__traits(compiles, __traits(getMember, I, Members[idx])))
mixin overloadMethods!(Members[idx]);
mixin allMethods!(idx+1);
}
}
private mixin template overloadMethods(string member) {
alias Overloads = AliasSeq!(__traits(getOverloads, I, member));
private static string impl()
{
import std.format : format;
string ret;
foreach (idx, F; Overloads) {
enum attribs = functionAttributeString!F(false);
enum is_prop = functionAttributes!F & FunctionAttribute.property;
ret ~= q{%s ReturnType!(Overloads[%s]) %s(%s) { return m_intf.%s(m_value, %s); }}
.format(attribs, idx, member, parameterDecls!(F, idx), member, parameterNames!F);
}
return ret;
}
mixin(impl());
}
private interface Proxy : staticMap!(ProxyOf, BaseTypeTuple!I) {
import std.meta : AliasSeq;
import std.traits : FunctionAttribute, MemberFunctionsTuple, ReturnType, ParameterTypeTuple, functionAttributes;
void _destroy(void[] stor) @safe nothrow;
void _postblit(void[] stor) @safe nothrow;
TypeInfo _typeInfo() @safe nothrow;
mixin methodDecls!0;
private mixin template methodDecls(size_t idx) {
alias Members = AliasSeq!(__traits(derivedMembers, I));
static if (idx < Members.length) {
static if (__traits(compiles, __traits(getMember, I, Members[idx])))
mixin overloadDecls!(Members[idx]);
mixin methodDecls!(idx+1);
}
}
private mixin template overloadDecls(string mem) {
alias Overloads = AliasSeq!(__traits(getOverloads, I, mem));
private static string impl()
{
import std.format : format;
string ret;
foreach (idx, F; Overloads) {
enum attribs = functionAttributeString!F(false);
enum vtype = functionAttributeThisType!F("void[]");
ret ~= q{ReturnType!(Overloads[%s]) %s(%s obj, %s) %s;}
.format(idx, mem, vtype, parameterDecls!(F, idx), attribs);
}
return ret;
}
mixin(impl());
}
}
static final class ProxyImpl(O) : Proxy {
static auto get()
{
static ProxyImpl impl;
if (!impl) impl = new ProxyImpl;
return impl;
}
override void _destroy(void[] stor)
@trusted nothrow {
static if (is(O == struct)) {
try destroy(_extract(stor));
catch (Exception e) assert(false, "Destructor has thrown: "~e.msg);
}
}
override void _postblit(void[] stor)
@trusted nothrow {
static if (is(O == struct)) {
try typeid(O).postblit(stor.ptr);
catch (Exception e) assert(false, "Postblit contructor has thrown: "~e.msg);
}
}
override TypeInfo _typeInfo()
@safe nothrow {
return typeid(O);
}
static ref inout(O) _extract(inout(void)[] stor)
@trusted nothrow pure @nogc {
if (stor.length < O.sizeof) assert(false);
return *cast(inout(O)*)stor.ptr;
}
mixin methodDefs!0;
private mixin template methodDefs(size_t idx) {
alias Members = AliasSeq!(__traits(allMembers, I));
static if (idx < Members.length) {
static if (__traits(compiles, __traits(getMember, I, Members[idx])))
mixin overloadDefs!(Members[idx]);
mixin methodDefs!(idx+1);
}
}
private mixin template overloadDefs(string mem) {
alias Overloads = AliasSeq!(__traits(getOverloads, I, mem));
private static string impl()
{
import std.format : format;
string ret;
foreach (idx, F; Overloads) {
alias R = ReturnType!F;
alias P = ParameterTypeTuple!F;
enum attribs = functionAttributeString!F(false);
enum vtype = functionAttributeThisType!F("void[]");
static if (is(R == void))
ret ~= q{override void %s(%s obj, %s) %s { _extract(obj).%s(%s); }}
.format(mem, vtype, parameterDecls!(F, idx), attribs, mem, parameterNames!F);
else
ret ~= q{override ReturnType!(Overloads[%s]) %s(%s obj, %s) %s { return _extract(obj).%s(%s); }}
.format(idx, mem, vtype, parameterDecls!(F, idx), attribs, mem, parameterNames!F);
}
return ret;
}
mixin(impl());
}
}
}
private string parameterDecls(alias F, size_t idx)()
{
import std.format : format;
import std.traits : ParameterTypeTuple, ParameterStorageClass, ParameterStorageClassTuple;
string ret;
alias PST = ParameterStorageClassTuple!F;
foreach (i, PT; ParameterTypeTuple!F) {
static if (i > 0) ret ~= ", ";
static if (PST[i] & ParameterStorageClass.scope_) ret ~= "scope ";
static if (PST[i] & ParameterStorageClass.out_) ret ~= "out ";
static if (PST[i] & ParameterStorageClass.ref_) ret ~= "ref ";
static if (PST[i] & ParameterStorageClass.lazy_) ret ~= "lazy ";
ret ~= format("ParameterTypeTuple!(Overloads[%s])[%s] param_%s", idx, i, i);
}
return ret;
}
private string parameterNames(alias F)()
{
import std.format : format;
import std.traits : ParameterTypeTuple;
string ret;
foreach (i, PT; ParameterTypeTuple!F) {
static if (i > 0) ret ~= ", ";
ret ~= format("param_%s", i);
}
return ret;
}
private alias ProxyOf(I) = InterfaceProxy!I.Proxy;
unittest {
static interface I {
@property int count() const;
}
static struct S {
int* cnt;
this(bool) { cnt = new int; *cnt = 1; }
this(this) { if (cnt) (*cnt)++; }
~this() { if (cnt) (*cnt)--; }
@property int count() const { return cnt ? *cnt : 0; }
}
auto s = S(true);
assert(s.count == 1);
auto t = interfaceProxy!I(s);
assert(s.count == 2);
t = interfaceProxy!I(s);
assert(s.count == 2);
t = s;
assert(s.count == 2);
s = S.init;
assert(t.count == 1);
s = t.extract!S;
assert(s.count == 2);
t = InterfaceProxy!I.init;
assert(s.count == 1);
t = s;
assert(s.count == 2);
s = S(true);
assert(s.count == 1);
assert(t.count == 1);
{
InterfaceProxy!I u;
u = s;
assert(u.count == 2);
}
assert(s.count == 1);
}