Signals and Slots in D

Georg Wrede georg.wrede at nospam.org
Fri Sep 29 03:18:45 PDT 2006


Walter Bright wrote:
> Walter Bright wrote:
> 
>> Some of the boilerplate can be eliminated with a mixin.
> 
> 
> Here's the mixin. Actually, 3 of them, one each for 0 arguments, 1 
> argument, and 2 arguments. I added a disconnect() function. Note how 
> trivial it is to use - no need for preprocessing.
> 
> import std.stdio;
> 
> template Signal()    // for 0 arguments
> {
>     void emit()
>     {
>         foreach (dg; slots)
>         dg();
>     }
> 
>     void connect( void delegate() dg)
>     {
>         slots ~= dg;
>     }
> 
>     void disconnect( void delegate() dg)
>     {
>     for (size_t i = 0; i < slots.length; i++)
>     {
>         if (slots[i] == dg)
>         {
>         if (i + 1 == slots.length)
>             slots = slots[0 .. i];
>         else
>             slots = slots[0 .. i] ~ slots[i + 1 .. length];
>         }
>     }
>     }
> 
>   private:
>     void delegate()[] slots;
> }
> 
> template Signal(T1)    // for one argument
> {
>     void emit( T1 i )
>     {
>         foreach (dg; slots)
>         dg(i);
>     }
> 
>     void connect( void delegate(T1) dg)
>     {
>         slots ~= dg;
>     }
> 
>     void disconnect( void delegate(T1) dg)
>     {
>     for (size_t i = 0; i < slots.length; i++)
>     {
>         if (slots[i] == dg)
>         {
>         if (i + 1 == slots.length)
>             slots = slots[0 .. i];
>         else
>             slots = slots[0 .. i] ~ slots[i + 1 .. length];
>         }
>     }
>     }
> 
>   private:
>     void delegate(T1)[] slots;
> }
> 
> template Signal(T1, T2) // for two arguments
> {
>     void emit( T1 i, T2 j )
>     {
>         foreach (dg; slots)
>         dg(i, j);
>     }
> 
>     void connect( void delegate(T1, T2) dg)
>     {
>         slots ~= dg;
>     }
> 
>     void disconnect( void delegate(T1, T2) dg)
>     {
>     for (size_t i = 0; i < slots.length; i++)
>     {
>         if (slots[i] == dg)
>         {
>         if (i + 1 == slots.length)
>             slots = slots[0 .. i];
>         else
>             slots = slots[0 .. i] ~ slots[i + 1 .. length];
>         }
>     }
>     }
> 
>   private:
>     void delegate(T1, T2)[] slots;
> }
> 
> 
> class Foo
> {
>     this() { }
> 
>     int value() { return val; }
> 
>     void setValue( int v )
>     {
>       if ( v != val )
>       {
>     val = v;
>     emit(v);
>       }
>     }
> 
>     mixin Signal!(int);    // adds in all the boilerplate to make it work
> 
>   private:
>     int val;
> }
> 
> void main()
> {
>     Foo a = new Foo;
>     Foo b = new Foo;
>     a.connect(&b.setValue);
>     b.setValue( 11 );     // a == 0  b == 11
>     a.setValue( 79 );     // a == 79 b == 79
>     writefln(b.value()); // prints 79
>     a.disconnect(&b.setValue);
>     a.setValue( 80);
>     writefln(b.value()); // prints 79
> }

One problem with this setup is that here every observee "knows" about 
all its observers.

If, on top of this, we want to give the observers the ability to 
unregister themselves (e.g. before getting destroyed), the observer has 
to know about all the observees.

This essentially creates a network with pointers.

Having instead an external entity to handle SS reduces drastically the 
number of needed connections.



More information about the Digitalmars-d mailing list