www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - signal slots (new, fixed version)

reply Lutger <lutger.blijdestijn gmail.com> writes:
After Bastiaan Veelo's post I realized my signal slots library should be 
rewritten, which is now mostly done. You can get the result or browse 
the docs here:
lutger.ifastnet.com

Unreferenced objects are now properly garbage collected and 
disconnected, emitting a signal (should) incurs less overhead,
both memory- and performance wise.

It still needs some testing and a little work, but it's functional.

Features:
- free functions, delegate literals and delegates can act as slots
- clean syntax, freestanding signals.
- emitting signals are locked to prevent possible stack overflow, can be 
turned off with version=Unsafe.
- can set a default handler, which is invoked when a signal is emitted 
that has no slots connected.
- signals can have return values.
- signals have opApply and opApplyReverse for custom iteration, mapping 
and combining return values.

Example:

import std.stdio, sslot.signal;

class Button
{
     this() { onClick = new Signal!(); }
     Signal!() onClick;
}

class Receiver : ISlotTracking
{
     void handleClick() { writefln("button clicked"); }
     mixin SlotTracking;
}

Button   button   = new Button;
Receiver receiver = new Receiver;
button.onClick.connect(&receiver.handleClick, receiver);
button.onClick();  // prints 'button clicked'

Signal!(int,int) signal = new Signal!(int,int);

signal ~= (int num) { return num * num; };
assert(signal(3) == 9);
Oct 27 2006
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Lutger wrote:
 After Bastiaan Veelo's post I realized my signal slots library should be 
 rewritten, which is now mostly done. You can get the result or browse 
 the docs here:
 lutger.ifastnet.com
 
 Unreferenced objects are now properly garbage collected and 
 disconnected, emitting a signal (should) incurs less overhead,
 both memory- and performance wise.
 
 It still needs some testing and a little work, but it's functional.
 
 Features:
 - free functions, delegate literals and delegates can act as slots
 - clean syntax, freestanding signals.
 - emitting signals are locked to prevent possible stack overflow, can be 
 turned off with version=Unsafe.
 - can set a default handler, which is invoked when a signal is emitted 
 that has no slots connected.
 - signals can have return values.
 - signals have opApply and opApplyReverse for custom iteration, mapping 
 and combining return values.
 
 Example:
 
 import std.stdio, sslot.signal;
 
 class Button
 {
     this() { onClick = new Signal!(); }
     Signal!() onClick;
 }
 
 class Receiver : ISlotTracking
 {
     void handleClick() { writefln("button clicked"); }
     mixin SlotTracking;
 }
 
 Button   button   = new Button;
 Receiver receiver = new Receiver;
 button.onClick.connect(&receiver.handleClick, receiver);
 button.onClick();  // prints 'button clicked'
 
 Signal!(int,int) signal = new Signal!(int,int);
 
 signal ~= (int num) { return num * num; };
 assert(signal(3) == 9);

It seems like having a return value in the slot that differs from the return value on the signal results in the slot silently not getting called. I'd like to be able to have a Signal!(void,int) but be able to call a method that returns something like "int method(int var)". A signal with void return should be able to just ignore the return value. At the very least the connect attempt should fail if the slot isn't going to actually get called. Other than that it seems to work pretty well. Is there no way the Signals can be initialized automatically? It's pretty ugly to have to do a new Signal in my constructor for every signal I want to declare. Actually I was getting crashes for the first little while before I realized I had to do that. --bb
Oct 30 2006
parent reply Lutger <lutger.blijdestijn gmail.com> writes:
Bill Baxter wrote:
  > It seems like having a return value in the slot that differs from the
 return value on the signal results in the slot silently not getting called.
 
 I'd like to be able to have a Signal!(void,int) but be able to call a 
 method that returns something like "int method(int var)".  A signal with 
 void return should be able to just ignore the return value.
 
 At the very least the connect attempt should fail if the slot isn't 
 going to actually get called.

I simply didn't think of that, thanks. The connection should fail with a compiler message, I'll fix it. It's possible, although perhaps not very safe, to cast delegates to the type the Signal can handle: signal.connect(cast(signal.DelegateSlot)method); // for delegates signal.connect(cast(signal.FunctionSlot)&method); // for functions This could be done inside Signal with parameter type checking, I'll consider it.
 Other than that it seems to work pretty well.

Good, thank you for this feedback, I still have to test it better.
 Is there no way the Signals can be initialized automatically?  It's 
 pretty ugly to have to do a new Signal in my constructor for every 
 signal I want to declare.  Actually I was getting crashes for the first 
 little while before I realized I had to do that.
 
 --bb

I would like that too, but I don't see an acceptable way to do it. A signal needs to do some things in the destructor such as notifying connected slots it does not exist anymore and freeing memory allocated on the C heap. Implementing a signal as a struct instead would require to call a deinit() function or something like that which I think is worse. One thing I was thinking about is to make the signal implementation available as a mixin too, with an explicit emit() function instead of overloaded opCall. This way you could give a class itself the signal functionality without the need for inheritance, in some cases it might be more convenient.
Oct 30 2006
parent reply Kristian <kjkilpi gmail.com> writes:
On Tue, 31 Oct 2006 05:41:52 +0200, Lutger <lutger.blijdestijn gmail.com>  
wrote:
 Bill Baxter wrote:

 Is there no way the Signals can be initialized automatically?  It's  
 pretty ugly to have to do a new Signal in my constructor for every  
 signal I want to declare.  Actually I was getting crashes for the first  
 little while before I realized I had to do that.
  --bb

I would like that too, but I don't see an acceptable way to do it. A signal needs to do some things in the destructor such as notifying connected slots it does not exist anymore and freeing memory allocated on the C heap. Implementing a signal as a struct instead would require to call a deinit() function or something like that which I think is worse.

If only there were ctors & dtors for structures... Or if there were 'local object members' for classes, just like in C++. For example: class Foo { Bar m_obj; //automatically constructed & destroyed with 'Foo' };
Nov 01 2006
parent reply Thomas Kuehne <thomas-dloop kuehne.cn> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Kristian schrieb am 2006-11-01:
 Or if there were 'local object members' for classes, just like in C++. For  
 example:

      class Foo {
          Bar m_obj;  //automatically constructed & destroyed with 'Foo'
      };

How about Object.notifyRegister and Object.notifyUnRegister? sample use: http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D.learn&artnum=5016 Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFFSKq/LK5blCcjpWoRAv0VAJ4hvZtQo+wZR64qRsgqlJVs29ZsSQCfe1Dt Z9/vZzgRPHl79zJzViJIkMU= =9HhU -----END PGP SIGNATURE-----
Nov 01 2006
parent reply Lutger <lutger.blijdestijn gmail.com> writes:
Thomas Kuehne wrote:
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1
 
 Kristian schrieb am 2006-11-01:
 Or if there were 'local object members' for classes, just like in C++. For  
 example:

      class Foo {
          Bar m_obj;  //automatically constructed & destroyed with 'Foo'
      };

How about Object.notifyRegister and Object.notifyUnRegister? sample use: http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D.learn&artnum=5016 Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFFSKq/LK5blCcjpWoRAv0VAJ4hvZtQo+wZR64qRsgqlJVs29ZsSQCfe1Dt Z9/vZzgRPHl79zJzViJIkMU= =9HhU -----END PGP SIGNATURE-----

Oh crap I wish I knew about this undocumented feature before. Thank you this could be very useful, at least until weak references are part of the language. I'll update the thing soon with some minor improvements and then start yet another rewrite / refactoring round.
Nov 01 2006
parent reply Sean Kelly <sean f4.ca> writes:
Lutger wrote:
 Thomas Kuehne wrote:
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1

 Kristian schrieb am 2006-11-01:
 Or if there were 'local object members' for classes, just like in 
 C++. For  example:

      class Foo {
          Bar m_obj;  //automatically constructed & destroyed with 'Foo'
      };

How about Object.notifyRegister and Object.notifyUnRegister? sample use: http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars D.learn&artnum=5016 Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFFSKq/LK5blCcjpWoRAv0VAJ4hvZtQo+wZR64qRsgqlJVs29ZsSQCfe1Dt Z9/vZzgRPHl79zJzViJIkMU= =9HhU -----END PGP SIGNATURE-----

Oh crap I wish I knew about this undocumented feature before. Thank you this could be very useful, at least until weak references are part of the language.

It was added in DMD 170-172, so it hasn't been around for long. Sean
Nov 01 2006
parent reply Walter Bright <newshound digitalmars.com> writes:
Sean Kelly wrote:
 Lutger wrote:
 Thomas Kuehne wrote:
 How about Object.notifyRegister and Object.notifyUnRegister? sample use:
 http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars
D.learn&artnum=5016 

you this could be very useful, at least until weak references are part of the language.

It was added in DMD 170-172, so it hasn't been around for long.

It was added to support signals and slots, which will be in the next update. The whole thing then becomes almost trivial (!)
Nov 01 2006
next sibling parent reply J Duncan <jtd514 nospam.ameritech.net> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 Lutger wrote:
 Thomas Kuehne wrote:
 How about Object.notifyRegister and Object.notifyUnRegister? sample 
 use:
 http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars
D.learn&artnum=5016 

Oh crap I wish I knew about this undocumented feature before. Thank you this could be very useful, at least until weak references are part of the language.

It was added in DMD 170-172, so it hasn't been around for long.

It was added to support signals and slots, which will be in the next update. The whole thing then becomes almost trivial (!)

You are the man! I have been experimenting with hooking into object destruction by replacing the dtor pointer in the classinfo. I came across the monitor reference today in the ABI docs and have been looking into it - wondering what you are up to. This is Very Cool! Thanks!
Nov 01 2006
parent reply Walter Bright <newshound digitalmars.com> writes:
J Duncan wrote:
 You are the man! I have been experimenting with hooking into object 
 destruction by replacing the dtor pointer in the classinfo.

I looked into that, too, and decided that it was never going to work. Everything I thought of just cost too much memory and runtime for all objects, not just hooked ones.
 I came 
 across the monitor reference today in the ABI docs and have been looking 
 into it - wondering what you are up to. This is Very Cool! Thanks!

I had a flash of inspiration one day that I was trying to hook in the wrong place. Since the monitor is an opaque type, that could be hooked with only a bit of casting, and it wouldn't affect anything else. Best of all, it only costs if it is used, not for the usual case. The monitors even still work with hooked objects. I didn't like having to tinker around under the hood like that, but it was in a good cause.
Nov 01 2006
parent Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 J Duncan wrote:
 You are the man! I have been experimenting with hooking into object 
 destruction by replacing the dtor pointer in the classinfo.

I looked into that, too, and decided that it was never going to work. Everything I thought of just cost too much memory and runtime for all objects, not just hooked ones.

I agree. Ares allows object destruction in general to be hooked, but it's really more for detecting memory 'leaks' (non-deterministic destruction) and for conditionally special handling of the destruction of certain object types. While I haven't used the new hooking mechanism yet, it does seem to be the most appropriate solution for signals/slots.
 I came across the monitor reference today in the ABI docs and have 
 been looking into it - wondering what you are up to. This is Very 
 Cool! Thanks!

I had a flash of inspiration one day that I was trying to hook in the wrong place. Since the monitor is an opaque type, that could be hooked with only a bit of casting, and it wouldn't affect anything else. Best of all, it only costs if it is used, not for the usual case. The monitors even still work with hooked objects.

Yup, this was definitely a nice way to do it. And it came with a rewrite of the monitor code to boot :-) I think the result is cleaner overall than what we had before. Sean
Nov 01 2006
prev sibling next sibling parent Lutger <lutger.blijdestijn gmail.com> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 Lutger wrote:
 Thomas Kuehne wrote:
 How about Object.notifyRegister and Object.notifyUnRegister? sample 
 use:
 http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars
D.learn&artnum=5016 

Oh crap I wish I knew about this undocumented feature before. Thank you this could be very useful, at least until weak references are part of the language.

It was added in DMD 170-172, so it hasn't been around for long.

It was added to support signals and slots, which will be in the next update. The whole thing then becomes almost trivial (!)

Now that is great! Didn't expect this so soon, I'll wait until then to see if this lib is still relevant. Some very useful features are getting into D lately, especially for library development.
Nov 01 2006
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 Lutger wrote:
 Thomas Kuehne wrote:
 How about Object.notifyRegister and Object.notifyUnRegister? sample 
 use:
 http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars
D.learn&artnum=5016 

Oh crap I wish I knew about this undocumented feature before. Thank you this could be very useful, at least until weak references are part of the language.

It was added in DMD 170-172, so it hasn't been around for long.

It was added to support signals and slots, which will be in the next update. The whole thing then becomes almost trivial (!)

Wow! Excellent. Any chance you'll run the API by the newsgroup for feedback before the actual release? --bb
Nov 01 2006
parent Walter Bright <newshound digitalmars.com> writes:
Bill Baxter wrote:
 Wow!  Excellent.  Any chance you'll run the API by the newsgroup for 
 feedback before the actual release?

I'd like to wait a bit first, I'm not ready yet.
Nov 01 2006