class Foo : XObject { @signal void message( string str ); } class Bar : XObject { @slot void print( string str ) { writefln( "Bar.print: %s", str ); } } void main() { auto a = new Foo, b = new Bar; connect( a.message, b.print ); a.message( "hello habr" ); // Bar.print: hello habr }
class Foo : XObject { mixin MixX; // , mixin @signal void _message( string str ) {} // , } class Bar : XObject { mixin MixX; // slot, void print( string str ) { writefln( "Bar.print: %s", str ); } } void main() { auto a = new Foo, b = new Bar; connect( a.signal_message, &b.print ); // a.message( "hello habr" ); // Bar.print: hello habr }
interface ContextHandler { protected: void selfDestroyCtx(); // public: @property { ContextHandler parentCH(); // ContextHandler[] childCH(); // } final { T registerCH(T)( T obj, bool force=true ) // if( is( T == class ) ) { if( auto ch = cast(ContextHandler)obj ) if( force || ( !force && ch.parentCH is null ) ) // force - obj ... return obj; } T newCH(T,Args...)( Args args ) { return registerCH( new T(args) ); } // void destroyCtx() // { foreach( c; childCH ) // c.destroyCtx(); selfDestroyCtx(); // } } }
interface SignalConnector // { void disconnect( SlotContext ); void disonnectAll(); } class SlotContext : ContextHandler // , { mixin MixContextHandler; // ContextHandler mixin template protected: size_t[SignalConnector] signals; // , public: void connect( SignalConnector sc ) { signals[sc]++; } void disconnect( SignalConnector sc ) { if( sc in signals ) { if( signals[sc] > 0 ) signals[sc]--; else signals.remove(sc); } } protected: void selfDestroyCtx() // { foreach( sig, count; signals ) sig.disconnect(this); } } // interface SlotHandler { SlotContext slotContext() @property; } class Slot(Args...) // { protected: Func func; // SlotContext ctrl; // public: alias Func = void delegate(Args); this( SlotContext ctrl, Func func ) { this.ctrl = ctrl; this.func = func; } this( SlotHandler hndl, Func func ) { this( hndl.slotContext, func ); } void opCall( Args args ) { func( args ); } SlotContext context() @property { return ctrl; } }
class Signal(Args...) : SignalConnector, ContextHandler { mixin MixContextHandler; protected: alias TSlot = Slot!Args; TSlot[] slots; // public: TSlot connect( TSlot s ) { if( !connected(s) ) { slots ~= s; s.context.connect(this); } return s; } void disconnect( TSlot s ) // { slots = slots.filter!(a=>a !is s).array; s.context.disconnect(this); } void disconnect( SlotContext sc ) // { foreach( s; slots.map!(a=>a.context).filter!(a=> a is sc) ) s.disconnect(this); slots = slots .map!(a=>tuple(a,a.context)) .filter!(a=> a[1] !is sc) .map!(a=>a[0]) .array; } void disconnect( SlotHandler sh ) { disconnect( sh.slotContext ); } void disonnectAll() // { slots = []; foreach( s; slots ) s.context.disconnect( this ); } // void opCall( Args args ) { foreach( s; slots ) s(args); } protected: bool connected( TSlot s ) { return canFind(slots,s); } void selfDestroyCtx() { disonnectAll(); } // }
interface Messager { void onMessage( string ); } class Drawable { abstract void onDraw(); } // class A : Drawable, XBase { mixin MixX; this() { prepareXBase(); } // @signal void _onDraw() {} } class B : A, Messager { mixin MixX; @signal void _onMessage( string msg ) {} } class Printer : XObject { mixin MixX; void print( string msg ) { } } auto a = new B; auto b = new B; auto p = new Printer; connect( a.signal_onMessage, &b.onMessage ); // connect( &p.print, b.signal_onMessage ); // connect ...
interface XBase : SlotHandler, ContextHandler { public: enum signal; // UDA, enum protected: void createSlotContext(); void createSignals(); final void prepareXBase() // , XBase { createSlotContext(); createSignals(); } // XBase SlotHandler, final auto newSlot(Args...)( void delegate(Args) f ) { return newCH!(Slot!Args)( this, f ); } // , , final auto connect(Args...)( Signal!Args sig, void delegate(Args) f ) { auto ret = newSlot!Args(f); sig.connect( ret ); return ret; } mixin template MixX() { import std.traits; // ++, mixin template , static if( !is(typeof(X_BASE_IMPL)) ) { enum X_BASE_IMPL = true; mixin MixContextHandler; // ContextHandler // SlotHandler private SlotContext __slot_context; final { public SlotContext slotContext() @property { return __slot_context; } protected void createSlotContext() { __slot_context = newCH!SlotContext; } } } // mixin defineSignals; // override protected { // createSignal , static if( isAbstractFunction!createSignals ) void createSignals() { mixin( mix.createSignalsMixinString!(typeof(this)) ); } else // , createSignals void createSignals() { super.createSignals(); // mix.createSignalsMixinString , mixin( mix.createSignalsMixinString!(typeof(this)) ); } } } ... }
static struct __MixHelper { import std.algorithm, std.array; enum NAME_RULE = "must starts with '_'"; static pure @safe: // bool testName( string s ) { return s[0] == '_'; } string getMixName( string s ) { return s[1..$]; } // , - string signalMixinString(T,alias temp)() @property { ... } // enum signal_prefix = "signal_"; // createSignals string createSignalsMixinString(T)() @property { auto signals = [ __traits(derivedMembers,T) ] .filter!(a=>a.startsWith(signal_prefix)); // , /+ signal_ + +/ return signals .map!(a=>format("%1$s = newCH!(typeof(%1$s));",a)) // signal_onSomething = newCH!(typeof(signal_onSomething); .join("\n"); // , } // template functionFmt(alias fun) if( isSomeFunction!fun ) { enum functionFmt = format( "%s %s%s", (ReturnType!fun).stringof, // __traits(identifier,fun), // (ParameterTypeTuple!fun).stringof ); // } } protected enum mix = __MixHelper.init;
// @signal defineSignalsImpl mixin template defineSignals() { mixin defineSignalsImpl!( typeof(this), getFunctionsWithAttrib!( typeof(this), signal ) ); } // , ( , ) mixin template defineSignalsImpl(T,list...) { static if( list.length == 0 ) {} // else static if( list.length > 1 ) { // " " mixin defineSignalsImpl!(T,list[0..$/2]); mixin defineSignalsImpl!(T,list[$/2..$]); } else mixin( mix.signalMixinString!(T,list[0]) ); // , }
string signalMixinString(T,alias temp)() @property { enum temp_name = __traits(identifier,temp); // - enum func_name = mix.getMixName( temp_name ); // // - @system enum temp_attribs = sort([__traits(getFunctionAttributes,temp)]).array; static assert( temp_attribs == ["@system"], format( "fail Mix X for '%s': template signal function allows only @system attrib", T.stringof ) ); // , static if( __traits(hasMember,T,func_name) ) { alias base = AT!(__traits(getMember,T,func_name)); // // static assert( isAbstractFunction!base, format( "fail Mix X for '%s': target signal function '%s' must be abstract in base class", T.stringof, func_name ) ); // @system enum base_attribs = sort([__traits(getFunctionAttributes,base)]).array; static assert( temp_attribs == ["@system"], format( "fail Mix X for '%s': target signal function allows only @system attrib", T.stringof ) ); enum need_override = true; } else enum need_override = false; enum signal_name = signal_prefix ~ func_name; // alias , enum args_define = format( "alias %sArgs = ParameterTypeTuple!%s;", func_name, temp_name ); enum temp_protection = __traits(getProtection,temp); // , - enum signal_define = format( "%s Signal!(%sArgs) %s;", temp_protection, func_name, signal_name ); // , opCall enum func_impl = format( "final %1$s %2$s void %3$s(%3$sArgs args) { %4$s(args); }", (need_override ? "override" : ""), temp_protection, func_name, signal_name ); // ( ), return [args_define, signal_define, func_impl].join("\n"); }
template getFunctionsWithAttrib(T, Attr) { // <b></b>: , T // alias getFunctionsWithAttrib = impl!( __traits(derivedMembers,T) ); enum AttrName = __traits(identifier,Attr); // std.typetuple , // staticMap / anySatisfy template isAttr(A) { template isAttr(T) { enum isAttr = __traits(isSame,T,A); } } // template impl( names... ) { alias empty = TypeTuple!(); static if( names.length == 1 ) { enum name = names[0]; // , __traits(derivedMembers,T) alias, // this , static if( __traits(compiles, { alias member = AT!(__traits(getMember,T,name)); } ) ) { // : alias some = __traits(...) // template AT(alias T) { alias AT = T; } alias member = AT!(__traits(getMember,T,name)); // , alias attribs = TypeTuple!(__traits(getAttributes,member)); // static if( anySatisfy!( isAttr!Attr, attribs ) ) { enum RULE = format( "%s must be a void function", AttrName ); // static assert( isSomeFunction!member, format( "fail mix X for '%s': %s, found '%s %s' with @%s attrib", T.stringof, RULE, typeof(member).stringof, name, AttrName ) ); // - void static assert( is( ReturnType!member == void ), format( "fail mix X for '%s': %s, found '%s' with @%s attrib", T.stringof, RULE, mix.functionFmt!member, AttrName ) ); // - _ static assert( mix.testName( name ), format( "fail mix X for '%s': @%s name %s", T.stringof, mix.functionFmt!member, AttrName, mix.NAME_RULE ) ); alias impl = member; // "" } else alias impl = empty; } else alias impl = empty; } else alias impl = TypeTuple!( impl!(names[0..$/2]), impl!(names[$/2..$]) ); } }
void connect(T,Args...)( Signal!Args sig, T delegate(Args) slot ) { auto slot_handler = cast(XBase)cast(Object)(slot.ptr); // enforce( slot_handler, "slot context is not XBase" ); // , void static if( is(T==void) ) slot_handler.connect( sig, slot ); else slot_handler.connect( sig, (Args args){ slot(args); } ); } void connect(T,Args...)( T delegate(Args) slot, Signal!Args sig ) { connect( sig, slot ); }
connect( a.message, b.print );
void connect!(alias sig, alias slot)() ...
does not allow to save the context, alias passes essentially Class.method where Class is the name of the class, not an object. And you need to enter additional. check for matching signal and slot arguments. A form with delegates void connect(T,Args...)( void delegate(Args) sig, T delegate(Args) slot ) { ... } // connect( &a.message, &b.print );
loses information about the class that contains the signal. I couldn’t find a function pointer (sig.funcptr) to display its name, and it would have happened at runtime, and the name of the signal object would need to be constructed somehow, and returned from the dictionary (SignalConnector [string]) did not look like would. On this implemented as implemented =)Source: https://habr.com/ru/post/261641/
All Articles