2016-11-08 13:48:38 +00:00
|
|
|
module vibe.internal.interfaceproxy;
|
|
|
|
|
|
|
|
import vibe.internal.traits;
|
2017-10-30 22:01:22 +00:00
|
|
|
import vibe.internal.freelistref;
|
|
|
|
import vibe.internal.allocator;
|
|
|
|
import std.algorithm.mutation : move, swap;
|
2016-11-08 13:48:38 +00:00
|
|
|
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); }
|
|
|
|
|
2017-10-30 22:01:22 +00:00
|
|
|
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); }
|
|
|
|
|
2017-01-26 22:53:18 +00:00
|
|
|
InterfaceProxy!I interfaceProxy(I, O)(O o) { return InterfaceProxy!I(o); }
|
|
|
|
|
2017-07-23 13:04:11 +00:00
|
|
|
private final class InterfaceProxyClass(I, O) : I
|
2016-11-08 13:48:38 +00:00
|
|
|
{
|
|
|
|
import std.meta : AliasSeq;
|
|
|
|
import std.traits : FunctionAttribute, MemberFunctionsTuple, ReturnType, ParameterTypeTuple, functionAttributes;
|
|
|
|
|
|
|
|
private {
|
|
|
|
O m_obj;
|
|
|
|
}
|
|
|
|
|
2017-10-30 22:01:22 +00:00
|
|
|
this(ref O obj) { swap(m_obj, obj); }
|
2016-11-08 13:48:38 +00:00
|
|
|
|
|
|
|
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 {
|
2017-10-30 22:01:22 +00:00
|
|
|
void*[4] m_value;
|
|
|
|
enum maxSize = m_value.length * m_value[0].sizeof;
|
2016-11-08 13:48:38 +00:00
|
|
|
Proxy m_intf;
|
|
|
|
}
|
|
|
|
|
|
|
|
this(IP : InterfaceProxy!J, J)(IP proxy) @safe
|
|
|
|
{
|
2017-10-30 22:01:22 +00:00
|
|
|
() @trusted {
|
|
|
|
swap(proxy.m_value, m_value);
|
|
|
|
proxy.m_intf = null;
|
|
|
|
} ();
|
2016-11-08 13:48:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this(O)(O object) @trusted
|
|
|
|
{
|
2017-10-30 22:01:22 +00:00
|
|
|
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.");
|
2016-11-08 13:48:38 +00:00
|
|
|
import std.conv : emplace;
|
|
|
|
m_intf = ProxyImpl!O.get();
|
2017-01-26 23:51:22 +00:00
|
|
|
static if (is(O == struct))
|
2017-10-30 22:01:22 +00:00
|
|
|
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);
|
2016-11-08 13:48:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
~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;
|
2017-10-30 22:01:22 +00:00
|
|
|
m_value[] = null;
|
2016-11-08 13:48:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
T extract(T)()
|
|
|
|
@trusted nothrow {
|
|
|
|
if (!m_intf || m_intf._typeInfo() !is typeid(T))
|
|
|
|
assert(false, "Extraction of wrong type from InterfaceProxy.");
|
2017-10-30 22:01:22 +00:00
|
|
|
return (cast(T[])m_value[0 .. T.sizeof/m_value[0].sizeof])[0];
|
2016-11-08 13:48:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2017-10-30 22:01:22 +00:00
|
|
|
m_value[] = proxy.m_value[];
|
|
|
|
proxy.m_intf = null;
|
2016-11-08 13:48:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void opAssign(O)(O object) @trusted
|
|
|
|
if (checkInterfaceConformance!(O, I) is null)
|
|
|
|
{
|
2017-10-30 22:01:22 +00:00
|
|
|
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.");
|
2016-11-08 13:48:38 +00:00
|
|
|
import std.conv : emplace;
|
|
|
|
clear();
|
|
|
|
m_intf = ProxyImpl!O.get();
|
2017-07-18 21:24:54 +00:00
|
|
|
if (is(O == class))
|
2017-10-30 22:01:22 +00:00
|
|
|
(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);
|
2016-11-08 13:48:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2017-01-25 21:40:07 +00:00
|
|
|
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);
|
2016-11-08 13:48:38 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
2017-07-18 21:24:54 +00:00
|
|
|
|
2016-11-08 13:48:38 +00:00
|
|
|
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[]");
|
2017-01-25 21:40:07 +00:00
|
|
|
ret ~= q{ReturnType!(Overloads[%s]) %s(%s obj, %s) %s;}
|
|
|
|
.format(idx, mem, vtype, parameterDecls!(F, idx), attribs);
|
2016-11-08 13:48:38 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
mixin(impl());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static final class ProxyImpl(O) : Proxy {
|
|
|
|
static auto get()
|
|
|
|
{
|
|
|
|
static ProxyImpl impl;
|
|
|
|
if (!impl) impl = new ProxyImpl;
|
|
|
|
return impl;
|
|
|
|
}
|
|
|
|
|
2016-12-10 13:21:57 +00:00
|
|
|
override void _destroy(void[] stor)
|
2016-11-08 13:48:38 +00:00
|
|
|
@trusted nothrow {
|
2016-12-10 13:21:57 +00:00
|
|
|
static if (is(O == struct)) {
|
|
|
|
try destroy(_extract(stor));
|
|
|
|
catch (Exception e) assert(false, "Destructor has thrown: "~e.msg);
|
|
|
|
}
|
2016-11-08 13:48:38 +00:00
|
|
|
}
|
|
|
|
|
2016-12-10 13:21:57 +00:00
|
|
|
override void _postblit(void[] stor)
|
2016-11-08 13:48:38 +00:00
|
|
|
@trusted nothrow {
|
2016-12-10 13:21:57 +00:00
|
|
|
static if (is(O == struct)) {
|
|
|
|
try typeid(O).postblit(stor.ptr);
|
|
|
|
catch (Exception e) assert(false, "Postblit contructor has thrown: "~e.msg);
|
|
|
|
}
|
2016-11-08 13:48:38 +00:00
|
|
|
}
|
|
|
|
|
2016-12-10 13:21:57 +00:00
|
|
|
override TypeInfo _typeInfo()
|
2016-11-08 13:48:38 +00:00
|
|
|
@safe nothrow {
|
|
|
|
return typeid(O);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ref inout(O) _extract(inout(void)[] stor)
|
|
|
|
@trusted nothrow pure @nogc {
|
2016-12-10 13:21:57 +00:00
|
|
|
if (stor.length < O.sizeof) assert(false);
|
|
|
|
return *cast(inout(O)*)stor.ptr;
|
2016-11-08 13:48:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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))
|
2017-01-25 21:40:07 +00:00
|
|
|
ret ~= q{override void %s(%s obj, %s) %s { _extract(obj).%s(%s); }}
|
|
|
|
.format(mem, vtype, parameterDecls!(F, idx), attribs, mem, parameterNames!F);
|
2016-11-08 13:48:38 +00:00
|
|
|
else
|
2017-01-25 21:40:07 +00:00
|
|
|
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);
|
2016-11-08 13:48:38 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
mixin(impl());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-25 21:40:07 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-11-08 13:48:38 +00:00
|
|
|
private alias ProxyOf(I) = InterfaceProxy!I.Proxy;
|