2309d自定义属性

原文
如果想要更强大示例,下面是使用UDA来轻松定义无缝回复是否成功回调的调用函数RPC模块的概念证明.

proxy.requestName().onSuccess((string str) {
    
    
    writefln("We got the name reply back from the server: %s", str);
}).onFailure((string errMsg) {
    
    
    writefln("Something went wrong! Error: %s", errMsg);
});

此例省略了系统的实际网络部分,而只是在目标对象上直接调用函数,并立即回复.在真实世界示例中,会序化函数请求及其参数,及指定要调用对象唯一标识,通过网络或线程传递,并在目标进程上解码并运行,然后同样序化返回回复.

可按调用者等待直到收到回复或失败同步,或定期处理回调的异步来调整系统.如,假设把简单的.async插入调用链,并指定超时其他选项.

除此外,该程序应完全编译并说明系统如何工作.

import std.stdio;
import std.format;
import std.traits;
import std.exception;
import util.extratraits;
class Person {
    
    
    mixin ProxyReceiver;
    string name;
    int age;
    this(string name, int age) {
    
    
        this.name = name;
        this.age = age;
    }
    @Proxy
    @Reply!(string)
    void requestName() {
    
    
        writefln("SERVER# User requested name");
        reply(name);
    }
    @Proxy
    @Reply!(bool)
    void setName(string s) {
    
    
        writefln("SERVER# Changed name to: %s", s);
        this.name = s;
        reply(true);
    }
    @Proxy
    @Reply!(int)
    @Failure!(string)
    void doubleEvenNumber(int x) {
    
    
        writefln("SERVER# User wants to double number: %s", x);
        if (!(x % 2)) reply(x * 2, x);
        else failure("Supplied number is not even.");
    }
}
struct Reply(RA...) {
    
    }
struct Failure(RA...) {
    
    }
mixin template ProxyReceiver() {
    
    
    Proxy!(typeof(this)) _proxy;
    void reply(string funcstr = __FUNCTION__, RA...)(RA rargs) {
    
    
        _proxy.reply!(funcstr)(rargs);
    }
    void failure(string funcstr = __FUNCTION__, RA...)(RA rargs) {
    
    
        _proxy.failure!(funcstr)(rargs);
    }
}
class Proxy(Destination) {
    
    
    Destination dest;
    private static abstract class Callback {
    
    
        MsgNum msgNum;
        double startTime, timeout;
    }
    private static final class CallbackT(alias FUNC) : Callback {
    
    
        static if (hasUDA!(FUNC, Reply)) {
    
    
            alias SUCCESS = void delegate(TemplateArgsOf!(getUDAs!(FUNC, Reply)[0]));
            SUCCESS successDG;
        }
        static if (hasUDA!(FUNC, Failure)) {
    
    
            alias FAILURE = void delegate(TemplateArgsOf!(getUDAs!(FUNC, Failure)[0]));
            FAILURE failureDG;
        }
    }
    alias MsgNum = uint;
    MsgNum nextMsgNum = 1;
    Callback[MsgNum] callbacks;
    MsgNum lastMessageNum;
    alias FIRE = void delegate();
     FIRE[MsgNum] pendingCalls; //在此处使用闭包来模拟`网络延迟`,以留出时间添加回调
 
    this(Destination dest) {
    
    
        this.dest = dest;
        dest._proxy = this;
    }
    void reply(string funcstr = __FUNCTION__, RA...)(RA rargs) {
    
    
        mixin(`alias FUNC = `~funcstr~`;`);
        alias FQN = fullyQualifiedName!FUNC;
        static assert(hasUDA!(FUNC, Reply), "No reply allowed for func: "~FQN);
        alias UDA = getUDAs!(FUNC, Reply)[0];
        alias RFUNC = void delegate(TemplateArgsOf!UDA);
         static assert(canCallFuncWithParameters!(RFUNC, RA), format("Invalid parameters for reply: %s (expected %s)", RA.
 stringof, Parameters!RFUNC.stringof));
        auto msgNum = lastMessageNum;
        auto p = msgNum in callbacks;
        enforce(p, format("Callback not found: #%s", msgNum));
        auto cbase = *p;
        callbacks.remove(msgNum);
        auto cb = cast(CallbackT!FUNC) cbase;
        enforce(cb, "Callback mismatch: could not cast to %s", CallbackT!FUNC.stringof);
        //writefln("准备用参数:%s调用cb(%s)闭包",rargs,cb);
        if (cb.successDG !is null) {
    
    
            cb.successDG(rargs);
            cb.successDG = null;
        }
    }
    void failure(string funcstr = __FUNCTION__, RA...)(RA rargs) {
    
    
        mixin(`alias FUNC = `~funcstr~`;`);
        alias FQN = fullyQualifiedName!FUNC;
        static assert(hasUDA!(FUNC, Failure), "No failure allowed for func: "~FQN);
        alias UDA = getUDAs!(FUNC, Failure)[0];
        alias RFUNC = void delegate(TemplateArgsOf!UDA);
         static assert(canCallFuncWithParameters!(RFUNC, RA), format("Invalid parameters for failure: %s (expected %s)", RA.
 stringof, Parameters!RFUNC.stringof));
        auto msgNum = lastMessageNum;
        auto p = msgNum in callbacks;
        enforce(p, format("Callback not found: #%s", msgNum));
        auto cbase = *p;
        callbacks.remove(msgNum);
        auto cb = cast(CallbackT!FUNC) cbase;
        enforce(cb, "Callback mismatch: could not cast to %s", CallbackT!FUNC.stringof);
        if (cb.failureDG !is null) {
    
    
            cb.failureDG(rargs);
            cb.failureDG = null;
        }
    }
    struct Outbound(alias FUNC) {
    
    
        this() @disable;
        this(this) @disable;
        ~this() {
    
    
             //writefln("~Outbound!%s [%s:%s]", FUNCNAME!FUNC, msgNum, fired);
            if (!fired)
                go();
        }
        alias FQN = fullyQualifiedName!FUNC;
        static if (hasUDA!(FUNC, Reply)) {
    
    
            alias SUCCESS = void delegate(TemplateArgsOf!(getUDAs!(FUNC, Reply)[0]));
        }
        static if (hasUDA!(FUNC, Failure)) {
    
    
            alias FAILURE = void delegate(TemplateArgsOf!(getUDAs!(FUNC, Failure)[0]));
        }
        private Proxy proxy;
        private MsgNum msgNum;
        private bool fired;
        private this(Proxy proxy, MsgNum n) {
    
    
            this.proxy = proxy;
            this.msgNum = n;
        }
        static if (is(SUCCESS))
        auto onSuccess(SUCCESS dg) scope return {
    
    
            static assert(is(SUCCESS), ("Reply not allowed for %s", FQN));
            CallbackT!FUNC cb;
            if (auto p = msgNum in proxy.callbacks) {
    
    
                cb = cast(CallbackT!FUNC) *p;
                assert(cb, "Callback type mismatch");
                assert(cb.successDG is null, "Success callback already set");
            } else {
    
    
                cb = new CallbackT!FUNC;
                cb.msgNum = msgNum;
                proxy.callbacks[cb.msgNum] = cb;
            }
            cb.successDG = dg;
            fired = true; //返回新结构,因此现已失效
            return Outbound(proxy, msgNum);
        }
        static if (is(FAILURE))
        auto onFailure(FAILURE dg) scope return {
    
    
            static assert(is(FAILURE), ("Failure not allowed for %s", FQN));
            CallbackT!FUNC cb;
            if (auto p = msgNum in proxy.callbacks) {
    
    
                cb = cast(CallbackT!FUNC) *p;
                assert(cb, "Callback type mismatch");
                assert(cb.failureDG is null, "Failure callback already set");
            } else {
    
    
                cb = new CallbackT!FUNC;
                cb.msgNum = msgNum;
                proxy.callbacks[cb.msgNum] = cb;
            }
            cb.failureDG = dg;
            fired = true; //返回新结构,因此现已失效
            return Outbound(proxy, msgNum);
        }
        void go() {
    
    
            if (fired) return;
            fired = true;
            if (auto fireDG = msgNum in proxy.pendingCalls) {
    
    
                proxy.pendingCalls.remove(msgNum);
                (*fireDG)();
            }
        }
    }
    auto opDispatch(string s, SA...)(SA sargs) {
    
    
        //writefln("opDispatch:<%s>%s",s,SA.stringof);
        alias FUNC = __traits(getMember, dest, s);
        alias FN = FUNCNAME!FUNC;
        alias FQN = fullyQualifiedName!FUNC;
        static if (!hasTemplateUDA!(FUNC, Proxy)) {
    
    
             pragma(msg, format("Cannot call function %s without @Proxy UDA", FQN)); //在`opDispatch`中难以取编译错误消息
            static assert(false);
        }
        alias PARAMS = Parameters!FUNC;
        static if (!canCallFuncWithParameters!(FUNC, SA)) {
    
    
            pragma(msg, format("Invalid parameters for proxy %s: expected %s, got %s", FQN, PARAMS.stringof, SA.stringof));
            static assert(false, "Invalid parameters");
        } else {
    
    
            auto msgNum = nextMsgNum++;
            auto outbound = Outbound!FUNC(this, msgNum);
            pendingCalls[msgNum] = {
    
    
                 //此闭包直接调用接收方对象.实际上,按发射器/接收器对拆分代理,并跨要重构`网络`序化消息,并在`目标对象`上远程调用接收器.
                lastMessageNum = msgNum;
                __traits(getMember, dest, s)(sargs);
            };
            return outbound;
        }
    }
}
void main() {
    
    
    auto person = new Person("bob", 34);
    auto proxy = new Proxy!Person(person);
    proxy.requestName().onSuccess((string str) {
    
    
        writefln("Client| Received name: %s", str);
    });
    proxy.doubleEvenNumber(4).onSuccess((int r, int orig) {
    
    
        writefln("Client| Received doubled number: %s (Original number was: %s)", r, orig);
    }).onFailure((string str) {
    
    
        writefln("Client| Error: %s", str);
    });
    proxy.doubleEvenNumber(3).onSuccess((int r, int orig) {
    
    
        writefln("Client| Received doubled number: %s (Original number was: %s)", r, orig);
    }).onFailure((string str) {
    
    
        writefln("Client| Error: %s", str);
    });
    assert(proxy.callbacks.length == 0, format("Unhandled callbacks: %s", proxy.callbacks));
}
.
My util/extratraits.d for some additional helper templates:
module util.extratraits;
import std.traits;
template FUNCNAME(F...) if (F.length == 1) {
    
    
    import std.string;
    enum FUNCNAME = fullyQualifiedName!(F[0])[ fullyQualifiedName!(F[0]).lastIndexOf('.')+1 .. $ ];
}
bool canCallFuncWithParameters(alias FUNC, SA...)() pure @safe nothrow {
    
    
    static if (SA.length > Parameters!FUNC.length || SA.length < numRequiredArguments!FUNC) {
    
    
        return false;
    }
    static foreach (idx, P; Parameters!FUNC) {
    
    
        static if (idx < SA.length && !isImplicitlyConvertible!(SA[idx], P)) {
    
    
            return false;
        }
    }
    return true;
}
size_t numRequiredArguments(alias FUNC)() pure @safe nothrow {
    
    
    size_t numReq = 0;
    static foreach (argi, PD; ParameterDefaults!FUNC) {
    
    
        static if (is(PD == void))
            numReq++;
        else
            return numReq;
    }
    return numReq;
}
string shortname()(string str) {
    
    
    import std.string;
    auto excl = str.indexOf('!');
    if (excl > 0) str = str[0 .. excl];
    return str[str.lastIndexOf('.')+1 .. $];
}
bool hasTemplateUDA(alias SYM, alias UDA)() {
    
    
    static foreach (idx, attr; __traits(getAttributes, SYM)) {
    
    
        static if (__traits(isTemplate, TemplateOf!UDA)) {
    
    
            static if (__traits(isSame, attr, TemplateOf!UDA))
                return true;
        } else {
    
    
            static if (__traits(isSame, attr, UDA))
                return true;
        }
    }
    return false;
}
template isMemberVariable(alias THIS, alias SYM) if (isAggregateType!THIS) {
    
    
     enum bool isMemberVariable = !(isFunction!SYM || isType!SYM || __traits(isTemplate, SYM)
    //|| hasStaticMember!(THIS, SYM.stringof)
 );
}

猜你喜欢

转载自blog.csdn.net/fqbqrr/article/details/132801078