0
点赞
收藏
分享

微信扫一扫

d事件系统


这是简单的​​"事件总线"​​​系统,其工作方式类似​​"信号​​",但更易使用.


​​​程序员​​​必须继承​​事件​​​对象,并在其中定义如下所示​​必要​​属性:

class EventChat : Event {
string msg;
this (ChatServer asrv, string amsg) { source = asrv; msg = amsg; }
}

然后必须使用​​.post()​​方法提交.可合并为一行:

(new EventChat(srv0, "message #0")).post;

要处理所有​​提交​​​(​​排队​​​)事件,必须调用​​processEvents()​​​函数.
现在最有趣:​​​接收和处理​​​事件.要​​接收​​​事件,程序员必须注册​​事件​​监听器:

addEventListener(null, (EventChat evt) {
import std.stdio;
writeln("server #", (cast(ChatServer)evt.source).id, ": ", evt.msg);
log ~= evt.msg;
});

注意其中的​​巧妙技巧​​​:指定要就在​​闭包​​​中抓​​事件类型​​​,且事件系统仅路由​​EventChat(及其子类)​​​到​​监听​​器.

在运行时,可以通过​​cast(MyObj)obj​​​转换来​​检查​​​对象类型,如果不能强制转换​​obj​​​为​​给定类型​​​,则可返回​​null​​​.事件系统在​​公共结构​​​中存储​​处理器​​:

struct EventListenerInfo {
TypeInfo_Class ti;
void delegate (/*Event*/void* e) dg;
//`e`是`Event`子类;
uint id;
}

在此,有​​类类型​​​,但没有​​类​​​自身.如果要​​cast(ti)obj​​​,编译器会抱怨.则如何模拟​​动态转换​​​呢?
好吧,编译器为此​​​转换​​​调用​​d运行时​​​函数,且该函数接受​​TypeInfo_Class​​​!所以​​模拟​​编译器动作:

//导入运行时强制转换函数
private extern(C) void* _d_dynamic_cast (Object o, ClassInfo c);
//并调用它!
auto cobj = _d_dynamic_cast(obj, ti);
//现在,如果`obj`类型不对,则`cobj`为`null`

当然,可在​​addEventListener()​​​中创建​​内部闭包​​​并在其中​​强制转换​​​,因为那里有​​确切​​​类型,但是这里不能展示该​​很酷​​​的强制转换技巧!​​;-)​

源码还包括​​"弱引用"​​​实现,因此为​​某些对象​​​添加事件侦听器不会使其"​​始终活动状态​​​".
现在,"​​​事件总线​​"的完整源码,如:

module eventbus;
//示例用法
class ChatServer {
uint id;
this (uint aid) { id = aid; }
}

class EventChat : Event {
string msg;
this (ChatServer asrv, string amsg) { source = asrv; msg = amsg; }
}

class ChatTextPane {
string[] log;
uint lid;

this () {
lid = addEventListener(null, (EventChat evt) {
import std.stdio;
writeln("#服务", (cast(ChatServer)evt.source).id, ": ", evt.msg);
log ~= evt.msg;
});
}

~this () { removeEventListener(lid); }
}


void main () {
auto srv0 = new ChatServer(0);
auto srv1 = new ChatServer(1);
// 文本面板接收所有聊天事件
auto textlog = new ChatTextPane();
// 仅接收`server1`聊天事件
addEventListener(srv1, (EventChat evt) {
assert(evt.source is srv1);
import std.stdio;
writeln("#1服务器: ", evt.msg);
});

// 现在发送一些事件
(new EventChat(srv0, "#0消息")).post;
(new EventChat(srv1, "#1消息")).post;

// 处理排队事件
processEvents();
}

//----------
public class Event {
Object source; //可为null

// 传播标志
enum PFlags : ubyte {
Eaten = 1U<<0,//已处理事件,但未取消
Cancelled = 1U<<1,
//已取消事件(可能*既*已处理又已取消!)
Posted = 1U<<7,//已提交事件
}
private ubyte flags;

final void post () {
if (posted) throw new Exception("不能提交已提交事件");
flags |= PFlags.Posted;
events ~= this;
}

final pure nothrow @safe @nogc:
void eat () { flags |= PFlags.Eaten; }
void cancel () { flags |= PFlags.Cancelled; }

const @property:
bool eaten () { return ((flags&(PFlags.Eaten|PFlags.Cancelled)) == PFlags.Eaten); }
bool cancelled () { return ((flags&PFlags.Cancelled) != 0); }
bool processed () { return (eaten || cancelled); }
bool posted () { return ((flags&PFlags.Posted) != 0); }
}


//返回可在`removereventListener()`中用的事件侦听器`ID`或0
public uint addEventListener(E:Event) (Object srcobj, void delegate (E evt) dg) {
if (dg is null) return 0;
foreach (ref EventListenerInfo eli; llist) {
if (typeid(E) == eli.ti && eli.dg is cast(EventListenerInfo.DgType)dg) return eli.id;
}
if (lastid == lastid.max) lastid = 1; //包装
llist ~= EventListenerInfo(typeid(E), cast(EventListenerInfo.DgType)dg, srcobj);
return llist[$-1].id;
}


//如果成功删除监听器,则返回`true`
//这是`@nogc`,因此可在`dtors`中调用
public bool removeEventListener (uint id) @nogc {
if (id == 0) return false;
foreach (ref EventListenerInfo eli; llist) {
if (eli.id == id) {
needListenerCleanup = true;
eli.id = 0;
eli.dg = null;
return true;
}
}
return false;
}


//调用它来处理所有排队事件
//注意,如果事件处理器一直在加事件,则该函数永远不会返回.
public void processEvents () {
if (events.length == 0) return;
cleanupListeners();
while (events.length > 0) {
auto evt = events.ptr[0];
foreach (immutable c; 1..events.length) events.ptr[c-1] = events.ptr[c];
events[$-1] = null;
events.length -= 1;
events.assumeSafeAppend;
try {
callEventListeners(evt);
} catch (Exception e) {
import std.stdio : stderr;
stderr.writefln("错误处理事件: %s", e.msg);
}
}
cleanupListeners();
}


// 私
private:

Event[] events; // 排队事件

//取`d运行时`动态转换函数
private extern(C) void* _d_dynamic_cast (Object o, ClassInfo c);
struct EventListenerInfo {
alias DgType = void delegate (/*Event*/void* e); // 实际上,`e`是任何`Event`子类
TypeInfo_Class ti;
DgType dg;
uint id;
Weak!Object srcobj;
this (TypeInfo_Class ati, DgType adg, Object sobj) {
ti = ati;
id = ++lastid;
dg = adg;
if (sobj !is null) srcobj = new Weak!Object(sobj);
}
}

uint lastid;
EventListenerInfo[] llist;
bool needListenerCleanup = false;
void cleanupListeners () {
if (!needListenerCleanup) return;
needListenerCleanup = false;
size_t pos = 0;
while (pos < llist.length) {
if (llist.ptr[pos].srcobj !is null && llist.ptr[pos].srcobj.empty) { llist.ptr[pos].id = 0; }
if (llist.ptr[pos].id == 0) {
foreach (immutable c; pos+1..llist.length) llist.ptr[c-1] = llist.ptr[c];
llist[$-1] = EventListenerInfo.init;
llist.length -= 1;
llist.assumeSafeAppend;
} else {
++pos;
}
}
}


void callEventListeners (Event evt) {
if (evt is null || evt.processed) return;
foreach (ref EventListenerInfo eli; llist) {
if (eli.id == 0) continue;
if (eli.srcobj !is null) {
//如果源对象死亡,则标记监听器为删除
if (eli.srcobj.empty) { needListenerCleanup = true; eli.id = 0; eli.dg = null; continue; }
if (evt.source is null) continue;
if (evt.source !is eli.srcobj.object) continue;
}
// 以下行用`TypeInfo_Class`来`cast(ObjType)evt`
auto ecc = _d_dynamic_cast(evt, eli.ti);
if (ecc !is null) {
eli.dg(ecc);
if (evt.processed) break;
}
}
}


// 线安弱引用实现
// 基于http://forum.dlang.org/thread/jjote0$1cql$1@digitalmars.com.
import core.atomic, core.memory;

private alias void delegate (Object) DEvent;
private extern (C) void rt_attachDisposeEvent (Object h, DEvent e);
private extern (C) void rt_detachDisposeEvent (Object h, DEvent e);

final class Weak(T : Object) {

// 用保守垃集技巧.不适用`压缩/复制`.如`D`添加`压缩`,则用`内置`弱引用.

private size_t mObject;
private size_t mPtr;
private size_t mHash;

this (T obj=null) @trusted {
hook(obj);
}

@property T object () const @trusted nothrow {
auto obj = cast(T)cast(void*)(atomicLoad(*cast(shared)&mObject)^0xa5a5a5a5u);
// 移动对象,至垃集堆,因而,可查询是否活着.
//注意,即使`objlocal`的强制转换和赋值
//未把对象放入堆栈,此调用也会.
//所以,这是安全的
if (obj !is null && GC.addrOf(cast(void*)obj)) return obj;
return null;
}

@property void object (T obj) @trusted {
auto oobj = cast(T)cast(void*)(atomicLoad(*cast(shared)&mObject)^0xa5a5a5a5u);
if (oobj !is null && GC.addrOf(cast(void*)oobj)) unhook(oobj);
oobj = null;
hook(obj);
}

@property bool empty () const @trusted nothrow {
return (object is null);
}

void clear () @trusted { object = null; }

void opAssign (T obj) @trusted { object = obj; }

private void hook (Object obj) @trusted {
if (obj !is null) {
//auto ptr = cast(size_t)cast(void*)obj;
// Andrej Mitrovic修复
auto ptr = cast(size_t)*(cast(void**)&obj);
// 使用原子,因为并非所有架构都可保证这些值的原子存储和加载
atomicStore(*cast(shared)&mObject, ptr^0xa5a5a5a5u);
// 只赋值一次,所以没有原子
mPtr = ptr^0xa5a5a5a5u;
mHash = typeid(T).getHash(&obj);
rt_attachDisposeEvent(obj, &unhook);
GC.setAttr(cast(void*)this, GC.BlkAttr.NO_SCAN);
} else {
atomicStore(*cast(shared)&mObject, cast(size_t)0^0xa5a5a5a5u);
}
}

private void unhook (Object obj) @trusted {
rt_detachDisposeEvent(obj, &unhook);
//该赋值很重要.
//如果收集时,不置`mObject`为空,则`垃集`为新对象`重用`内存时,对象中的检查,可能返回`假阳`性.
atomicStore(*cast(shared)&mObject, cast(size_t)0^0xa5a5a5a5u);
}

override bool opEquals (Object o) @trusted nothrow {
if (this is o) return true;
if (auto weak = cast(Weak!T)o) return (mPtr == weak.mPtr);
return false;
}

override int opCmp (Object o) @trusted nothrow {
if (auto weak = cast(Weak!T)o) return (mPtr > weak.mPtr ? 1 : mPtr < weak.mPtr ? -1 : 0);
return 1;
}

override size_t toHash () @trusted nothrow {
auto obj = object;
return (obj ? typeid(T).getHash(&obj) : mHash);
}

override string toString () @trusted {
auto obj = object;
return (obj ? obj.toString() : toString());
}
}



举报

相关推荐

0 条评论