Super lightweight signal and slots implementation using macros

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Super lightweight signal and slots implementation using macros

Mario Carbajal
There's something that has always bugged me about signals and slots as implemented in hsl or hxs: they need at least one heap allocated object per signal. The cost of allocating an object which uses signals and slots will increase with the number of different signals it may dispatch.

Yesterday I implemented a small class-building macro which implements very lightweight signals and slots with really low amounts of boilerplate code.

You use it the following way:

@:build(hxsigslot.SigSlot.build( {
    // Signals are defined here as function definitions, the return type and body are ignored by the macro.
    function TestSignal( myStringArg : String, myIntArg : Int ) { }
    function AnotherTestSignal() { }

})) class TestClass {

   // Your class code here

}

For each signal the macro will add a bind_SignalName_ and dispatch_SignalName_ method to your class:

bindTestSignal( listener : String -> Int -> Void ) : Void;
dispatchTestSignal( myStringArg : String, myIntArg : Int ) : Void;

bindAnotherTestSignal( listener : Void -> Void ) : Void;
dispatchAnotherTestSignal() : Void;

Usage is then pretty simple:

var test = new TestClass();
test.bindTestSignal( myTestSignalListener );
test.dispatchTestSignal( "hello", 2 );


Appart from adding this public methods the macro will also add this private member:

private var _sigslot_listenersHash : Hash<Array<Dynamic>>;

This hash will store the subscribers of each signal, it will be automatically created when a bind method is called. In contrast other signals and slots implementations would require you to add one member per dispatchable signal and usually initialize them all on construction.


The code is here:
Main.hx has a working example.

I would love to get some feedback since I'm not completely sure if this is actually a good idea. Maybe some of you can spot problems with the idea or with my implementation, that would be very helpful :)

--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Super lightweight signal and slots implementation using macros

Dion Whitehead Amago
Interesting idea.

In my experience, dealing with hundreds of game objects each with (on
average) tens of listeners, the cost of signals, e.g. in hxhsl, pales
(it's insignificant) in comparison to the cost of rendering anything
interesting.  So while you may save some smidgen in pure logic
performance, the gains are usually irrelevant in the overall scheme.
Often when you're knee-deep in code, it won't seem that way, because
you have lots of code dealing with signals.

But cool idea.  Don't forget to add listener unsubscription, and for
the purposes of debugging, checking if listeners exist, or you might
have hanging references.

Dion

On Sun, Apr 10, 2011 at 1:17 PM, Mario Carbajal <[hidden email]> wrote:

> There's something that has always bugged me about signals and slots as
> implemented in hsl or hxs: they need at least one heap allocated object per
> signal. The cost of allocating an object which uses signals and slots will
> increase with the number of different signals it may dispatch.
>
> Yesterday I implemented a small class-building macro which implements very
> lightweight signals and slots with really low amounts of boilerplate code.
> You use it the following way:
> @:build(hxsigslot.SigSlot.build( {
>     // Signals are defined here as function definitions, the return type and
> body are ignored by the macro.
>     function TestSignal( myStringArg : String, myIntArg : Int ) { }
>     function AnotherTestSignal() { }
> })) class TestClass {
>
>    // Your class code here
>
> }
>
> For each signal the macro will add a bind_SignalName_ and
> dispatch_SignalName_ method to your class:
> bindTestSignal( listener : String -> Int -> Void ) : Void;
> dispatchTestSignal( myStringArg : String, myIntArg : Int ) : Void;
> bindAnotherTestSignal( listener : Void -> Void ) : Void;
> dispatchAnotherTestSignal() : Void;
> Usage is then pretty simple:
> var test = new TestClass();
> test.bindTestSignal( myTestSignalListener );
> test.dispatchTestSignal( "hello", 2 );
>
> Appart from adding this public methods the macro will also add this private
> member:
> private var _sigslot_listenersHash : Hash<Array<Dynamic>>;
> This hash will store the subscribers of each signal, it will be
> automatically created when a bind method is called. In contrast other
> signals and slots implementations would require you to add one member per
> dispatchable signal and usually initialize them all on construction.
>
> The code is here:
> http://code.google.com/p/hxsigslot/source/browse/
> Main.hx has a working example.
> I would love to get some feedback since I'm not completely sure if this is
> actually a good idea. Maybe some of you can spot problems with the idea or
> with my implementation, that would be very helpful :)
> --
> haXe - an open source web programming language
> http://haxe.org
>

--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Super lightweight signal and slots implementation using macros

Pimm Hogeling
In reply to this post by Mario Carbajal
Hey Mario,

Awesome contribution!

I like the idea of using macros to reduce boilerplate code and increase performance. I'll look into your codebase.

Thanks for sharing.

On 10 April 2011 20:17, Mario Carbajal <[hidden email]> wrote:
There's something that has always bugged me about signals and slots as implemented in hsl or hxs: they need at least one heap allocated object per signal. The cost of allocating an object which uses signals and slots will increase with the number of different signals it may dispatch.

Yesterday I implemented a small class-building macro which implements very lightweight signals and slots with really low amounts of boilerplate code.

You use it the following way:

@:build(hxsigslot.SigSlot.build( {
    // Signals are defined here as function definitions, the return type and body are ignored by the macro.
    function TestSignal( myStringArg : String, myIntArg : Int ) { }
    function AnotherTestSignal() { }

})) class TestClass {

   // Your class code here

}

For each signal the macro will add a bind_SignalName_ and dispatch_SignalName_ method to your class:

bindTestSignal( listener : String -> Int -> Void ) : Void;
dispatchTestSignal( myStringArg : String, myIntArg : Int ) : Void;

bindAnotherTestSignal( listener : Void -> Void ) : Void;
dispatchAnotherTestSignal() : Void;

Usage is then pretty simple:

var test = new TestClass();
test.bindTestSignal( myTestSignalListener );
test.dispatchTestSignal( "hello", 2 );


Appart from adding this public methods the macro will also add this private member:

private var _sigslot_listenersHash : Hash<Array<Dynamic>>;

This hash will store the subscribers of each signal, it will be automatically created when a bind method is called. In contrast other signals and slots implementations would require you to add one member per dispatchable signal and usually initialize them all on construction.


The code is here:
Main.hx has a working example.

I would love to get some feedback since I'm not completely sure if this is actually a good idea. Maybe some of you can spot problems with the idea or with my implementation, that would be very helpful :)

--
haXe - an open source web programming language
http://haxe.org


--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Super lightweight signal and slots implementation using macros

Tarwin Stroh-Spijer
I'm not sure if I'm reading this wrong, but it looks more like even listeners, as your passing through string names no? Or am I totally confused.

It would be nice to see an example that emulates the same process as was in the original Penner Signals. I was going to link the example but it seems his AS3 Signals have become, verbose, at least in the test department. I'm thinking of something like:

Superhero
 - punch (signal)
 - kick (signal)

With listeners. I find these "physical" examples really easy to understand.

Love the idea though, just want to be able to understand it easily!


Tarwin Stroh-Spijer
_______________________

Touch My Pixel
http://www.touchmypixel.com/
phone: +61 3 8060 5321
_______________________


On Mon, Apr 11, 2011 at 9:13 AM, Pimm Hogeling <[hidden email]> wrote:
Hey Mario,

Awesome contribution!

I like the idea of using macros to reduce boilerplate code and increase performance. I'll look into your codebase.

Thanks for sharing.

On 10 April 2011 20:17, Mario Carbajal <[hidden email]> wrote:
There's something that has always bugged me about signals and slots as implemented in hsl or hxs: they need at least one heap allocated object per signal. The cost of allocating an object which uses signals and slots will increase with the number of different signals it may dispatch.

Yesterday I implemented a small class-building macro which implements very lightweight signals and slots with really low amounts of boilerplate code.

You use it the following way:

@:build(hxsigslot.SigSlot.build( {
    // Signals are defined here as function definitions, the return type and body are ignored by the macro.
    function TestSignal( myStringArg : String, myIntArg : Int ) { }
    function AnotherTestSignal() { }

})) class TestClass {

   // Your class code here

}

For each signal the macro will add a bind_SignalName_ and dispatch_SignalName_ method to your class:

bindTestSignal( listener : String -> Int -> Void ) : Void;
dispatchTestSignal( myStringArg : String, myIntArg : Int ) : Void;

bindAnotherTestSignal( listener : Void -> Void ) : Void;
dispatchAnotherTestSignal() : Void;

Usage is then pretty simple:

var test = new TestClass();
test.bindTestSignal( myTestSignalListener );
test.dispatchTestSignal( "hello", 2 );


Appart from adding this public methods the macro will also add this private member:

private var _sigslot_listenersHash : Hash<Array<Dynamic>>;

This hash will store the subscribers of each signal, it will be automatically created when a bind method is called. In contrast other signals and slots implementations would require you to add one member per dispatchable signal and usually initialize them all on construction.


The code is here:
Main.hx has a working example.

I would love to get some feedback since I'm not completely sure if this is actually a good idea. Maybe some of you can spot problems with the idea or with my implementation, that would be very helpful :)

--
haXe - an open source web programming language
http://haxe.org


--
haXe - an open source web programming language
http://haxe.org


--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Super lightweight signal and slots implementation using macros

Mario Carbajal
@Dion Amago:
You may be right about the performance increase being minimal, but the flash GC makes me a bit paranoid. I'm not very knowledgeable about it's inner workings but I understand that it must iterate through all the allocated objects in memory, and reducing the number of new allocations and total allocated objects can increase the performance of a flash application. My paranoia is also fueled by articles like this one: http://tomgabob.blogspot.com/2009/11/as3-memory-management.html

I'll be adding both unbind and checking for repeated registrations soon.

@Tarwin Stroh-Spijer:
They are more similar to signals and slots than event listeners, the interface does not use strings and everything is as type safe as it can be. 
Although it is really simple ( just one signal with no arguments ) I hope it helps clear things up :)

On Sun, Apr 10, 2011 at 8:31 PM, Tarwin Stroh-Spijer <[hidden email]> wrote:
I'm not sure if I'm reading this wrong, but it looks more like even listeners, as your passing through string names no? Or am I totally confused.

It would be nice to see an example that emulates the same process as was in the original Penner Signals. I was going to link the example but it seems his AS3 Signals have become, verbose, at least in the test department. I'm thinking of something like:

Superhero
 - punch (signal)
 - kick (signal)

With listeners. I find these "physical" examples really easy to understand.

Love the idea though, just want to be able to understand it easily!


Tarwin Stroh-Spijer
_______________________

Touch My Pixel
http://www.touchmypixel.com/
phone: +61 3 8060 5321
_______________________



On Mon, Apr 11, 2011 at 9:13 AM, Pimm Hogeling <[hidden email]> wrote:
Hey Mario,

Awesome contribution!

I like the idea of using macros to reduce boilerplate code and increase performance. I'll look into your codebase.

Thanks for sharing.

On 10 April 2011 20:17, Mario Carbajal <[hidden email]> wrote:
There's something that has always bugged me about signals and slots as implemented in hsl or hxs: they need at least one heap allocated object per signal. The cost of allocating an object which uses signals and slots will increase with the number of different signals it may dispatch.

Yesterday I implemented a small class-building macro which implements very lightweight signals and slots with really low amounts of boilerplate code.

You use it the following way:

@:build(hxsigslot.SigSlot.build( {
    // Signals are defined here as function definitions, the return type and body are ignored by the macro.
    function TestSignal( myStringArg : String, myIntArg : Int ) { }
    function AnotherTestSignal() { }

})) class TestClass {

   // Your class code here

}

For each signal the macro will add a bind_SignalName_ and dispatch_SignalName_ method to your class:

bindTestSignal( listener : String -> Int -> Void ) : Void;
dispatchTestSignal( myStringArg : String, myIntArg : Int ) : Void;

bindAnotherTestSignal( listener : Void -> Void ) : Void;
dispatchAnotherTestSignal() : Void;

Usage is then pretty simple:

var test = new TestClass();
test.bindTestSignal( myTestSignalListener );
test.dispatchTestSignal( "hello", 2 );


Appart from adding this public methods the macro will also add this private member:

private var _sigslot_listenersHash : Hash<Array<Dynamic>>;

This hash will store the subscribers of each signal, it will be automatically created when a bind method is called. In contrast other signals and slots implementations would require you to add one member per dispatchable signal and usually initialize them all on construction.


The code is here:
Main.hx has a working example.

I would love to get some feedback since I'm not completely sure if this is actually a good idea. Maybe some of you can spot problems with the idea or with my implementation, that would be very helpful :)

--
haXe - an open source web programming language
http://haxe.org


--
haXe - an open source web programming language
http://haxe.org


--
haXe - an open source web programming language
http://haxe.org


--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Super lightweight signal and slots implementation using macros

Dion Whitehead Amago
True enough.  I usually recycle the objects holding the signalers,
rather than the signalers themselves.  Same result.

On Sun, Apr 10, 2011 at 11:03 PM, Mario Carbajal <[hidden email]> wrote:
> @Dion Amago:
> You may be right about the performance increase being minimal, but the flash
> GC makes me a bit paranoid. I'm not very knowledgeable about it's inner
> workings but I understand that it must iterate through all the allocated
> objects in memory, and reducing the number of new allocations and total
> allocated objects can increase the performance of a flash application. My
> paranoia is also fueled by articles like this one:
> http://tomgabob.blogspot.com/2009/11/as3-memory-management.html

--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Super lightweight signal and slots implementation using macros

Tarwin Stroh-Spijer
Thanks, that example is very clear. Looks promising!


Tarwin Stroh-Spijer
_______________________

Touch My Pixel
http://www.touchmypixel.com/
phone: +61 3 8060 5321
_______________________


On Mon, Apr 11, 2011 at 2:21 PM, Dion Amago <[hidden email]> wrote:
True enough.  I usually recycle the objects holding the signalers,
rather than the signalers themselves.  Same result.

On Sun, Apr 10, 2011 at 11:03 PM, Mario Carbajal <[hidden email]> wrote:
> @Dion Amago:
> You may be right about the performance increase being minimal, but the flash
> GC makes me a bit paranoid. I'm not very knowledgeable about it's inner
> workings but I understand that it must iterate through all the allocated
> objects in memory, and reducing the number of new allocations and total
> allocated objects can increase the performance of a flash application. My
> paranoia is also fueled by articles like this one:
> http://tomgabob.blogspot.com/2009/11/as3-memory-management.html

--
haXe - an open source web programming language
http://haxe.org


--
haXe - an open source web programming language
http://haxe.org
Reply | Threaded
Open this post in threaded view
|

Re: Super lightweight signal and slots implementation using macros

Pimm Hogeling
In reply to this post by Mario Carbajal
On 11 April 2011 06:03, Mario Carbajal <[hidden email]> wrote:
I'll be adding both unbind and checking for repeated registrations soon.
HSL currently has bonds, which are objects that represent the connection between the subject and the observer.

In addition to this:
dog.barkedSignaler.bind(soundPlayer.playBarkSound);
and then later on:
dog.barkedSignaler.unbind(soundPlayer.playBarkSound);

It allows you to do this:
var soundPlayerBond = dog.barkedSignaler.bind(soundPlayer.playBarkSound);
and then later on:
soundPlayerBond.destroy();

This functionality has been proven very useful over time (because you don't need a reference to dog nor soundPlayer to destroy the bond, all required information is encapsulated), especially when using anonymous functions. You should consider adding it. Also, I'll be contacting you soon to see if we can join forces in one library, instead of two separate ones.

--
haXe - an open source web programming language
http://haxe.org