Mercurial > projects > ldc
diff tango/tango/core/Signal.d @ 132:1700239cab2e trunk
[svn r136] MAJOR UNSTABLE UPDATE!!!
Initial commit after moving to Tango instead of Phobos.
Lots of bugfixes...
This build is not suitable for most things.
author | lindquist |
---|---|
date | Fri, 11 Jan 2008 17:57:40 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tango/tango/core/Signal.d Fri Jan 11 17:57:40 2008 +0100 @@ -0,0 +1,276 @@ +/** + * The signal module provides a basic implementation of the listener pattern + * using the "Signals and Slots" model from Qt. + * + * Copyright: Copyright (C) 2005-2006 Sean Kelly. All rights reserved. + * License: BSD style: $(LICENSE) + * Authors: Sean Kelly + */ +module tango.core.Signal; + + +private import tango.core.Array; + + +/** + * A signal is an event which contains a collection of listeners (called + * slots). When a signal is called, that call will be propagated to each + * attached slot in a synchronous manner. It is legal for a slot to call a + * signal's attach and detach methods when it is signaled. When this occurs, + * attach events will be queued and processed after the signal has propagated + * to all slots, but detach events are processed immediately. This ensures + * that it is safe for slots to be deleted at any time, even within a slot + * routine. + * + * Example: + * ----------------------------------------------------------------------------- + * + * class Button + * { + * Signal!(Button) press; + * } + * + * void wasPressed( Button b ) + * { + * printf( "Button was pressed.\n" ); + * } + * + * Button b = new Button; + * + * b.press.attach( &wasPressed ); + * b.press( b ); + * + * ----------------------------------------------------------------------------- + * + * Please note that this implementation does not use weak pointers to store + * references to slots. This design was chosen because weak pointers are + * inherently unsafe when combined with non-deterministic destruction, with + * many of the same limitations as destructors in the same situation. It is + * still possible to obtain weak-pointer behavior, but this must be done + * through a proxy object instead. + */ +struct Signal( Args... ) +{ + alias void delegate(Args) SlotDg; /// + alias void function(Args) SlotFn; /// + + alias opCall call; /// Alias to simplify chained calling. + + + /** + * The signal procedure. When called, each of the attached slots will be + * called synchronously. + * + * args = The signal arguments. + */ + void opCall( Args args ) + { + synchronized + { + m_blk = true; + + for( size_t i = 0; i < m_dgs.length; ++i ) + { + if( m_dgs[i] !is null ) + m_dgs[i]( args ); + } + m_dgs.length = m_dgs.remove( cast(SlotDg) null ); + + for( size_t i = 0; i < m_fns.length; ++i ) + { + if( m_fns[i] !is null ) + m_fns[i]( args ); + } + m_fns.length = m_fns.remove( cast(SlotFn) null ); + + m_blk = false; + + procAdds(); + } + } + + + /** + * Attaches a delegate to this signal. A delegate may be either attached + * or detached, so successive calls to attach for the same delegate will + * have no effect. + * + * dg = The delegate to attach. + */ + void attach( SlotDg dg ) + { + synchronized + { + if( m_blk ) + { + m_add ~= Add( dg ); + } + else + { + auto pos = m_dgs.find( dg ); + if( pos == m_dgs.length ) + m_dgs ~= dg; + } + } + } + + + /** + * Attaches a function to this signal. A function may be either attached + * or detached, so successive calls to attach for the same function will + * have no effect. + * + * fn = The function to attach. + */ + void attach( SlotFn fn ) + { + synchronized + { + if( m_blk ) + { + m_add ~= Add( fn ); + } + else + { + auto pos = m_fns.find( fn ); + if( pos == m_fns.length ) + m_fns ~= fn; + } + } + } + + + /** + * Detaches a delegate from this signal. + * + * dg = The delegate to detach. + */ + void detach( SlotDg dg ) + { + synchronized + { + auto pos = m_dgs.find( dg ); + if( pos < m_dgs.length ) + m_dgs[pos] = null; + } + } + + + /** + * Detaches a function from this signal. + * + * fn = The function to detach. + */ + void detach( SlotFn fn ) + { + synchronized + { + auto pos = m_fns.find( fn ); + if( pos < m_fns.length ) + m_fns[pos] = null; + } + } + + +private: + struct Add + { + enum Type + { + DG, + FN + } + + static Add opCall( SlotDg d ) + { + Add e; + e.ty = Type.DG; + e.dg = d; + return e; + } + + static Add opCall( SlotFn f ) + { + Add e; + e.ty = Type.FN; + e.fn = f; + return e; + } + + union + { + SlotDg dg; + SlotFn fn; + } + Type ty; + } + + + void procAdds() + { + foreach( a; m_add ) + { + if( a.ty == Add.Type.DG ) + m_dgs ~= a.dg; + else + m_fns ~= a.fn; + } + m_add.length = 0; + } + + + SlotDg[] m_dgs; + SlotFn[] m_fns; + Add[] m_add; + bool m_blk; +} + + +debug( UnitTest ) +{ + unittest + { + class Button + { + Signal!(Button) press; + } + + int count = 0; + + void wasPressedA( Button b ) + { + ++count; + } + + void wasPressedB( Button b ) + { + ++count; + } + + Button b = new Button; + + b.press.attach( &wasPressedA ); + b.press( b ); + assert( count == 1 ); + + count = 0; + b.press.attach( &wasPressedB ); + b.press( b ); + assert( count == 2 ); + + count = 0; + b.press.attach( &wasPressedA ); + b.press( b ); + assert( count == 2 ); + + count = 0; + b.press.detach( &wasPressedB ); + b.press( b ); + assert( count == 1 ); + + count = 0; + b.press.detach( &wasPressedA ); + b.press( b ); + assert( count == 0 ); + } +}