Mercurial > projects > ldc
view 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 source
/** * 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 ); } }