View mode: basic / threaded / horizontal-split · Log in · Help
October 27, 2006
signal slots (new, fixed version)
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);
October 28, 2006
interface + mixin combination
Looking at Lutger's new sigslot code, it occurred to me that this is 
probably a pretty common (and useful) combination:

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

I.e. using both a) derivation from an interface and b) a mixin.

I've seen similar C++ GUI libraries, where a macro takes the place of 
the mixin.   Examples I know of include

Qt - derive from QObject and use the QOBJECT macro
wx - derive from wxObject and use the DECLARE_CLASS macro
FOX - derive from FXObject and use FXDECLARE macro

It's a useful combo which is similar to regular class inheritance, but 
different in that the implementation/behavior is defined locally and 
non-virtually rather than being inherited from the base class.

The obvious syntax for it would be to just allow 'mixin' to appear in an 
interface.

I.e. for Lutger's code:
interface ISlotTracking
{
	void _registerCallback(void delegate(Object));
	void _removeCallbacksFrom(Object object);
	mixin SlotTracking;
}

It's basically a replacement for multiple inheritance.

Would it be useful?  Any issues I've overlooked?

--bb
October 28, 2006
Re: interface + mixin combination
Bill Baxter wrote:
> Looking at Lutger's new sigslot code, it occurred to me that this is 
> probably a pretty common (and useful) combination:
> 
>> class Receiver : ISlotTracking
>> {
>>     void handleClick() { writefln("button clicked"); }
>>     mixin SlotTracking;
>> }
> 
> 
> I.e. using both a) derivation from an interface and b) a mixin.
> 
> I've seen similar C++ GUI libraries, where a macro takes the place of 
> the mixin.   Examples I know of include
> 
> Qt - derive from QObject and use the QOBJECT macro
> wx - derive from wxObject and use the DECLARE_CLASS macro
> FOX - derive from FXObject and use FXDECLARE macro
> 
> It's a useful combo which is similar to regular class inheritance, but 
> different in that the implementation/behavior is defined locally and 
> non-virtually rather than being inherited from the base class.
> 
> The obvious syntax for it would be to just allow 'mixin' to appear in an 
> interface.
> 
> I.e. for Lutger's code:
> interface ISlotTracking
> {
>     void _registerCallback(void delegate(Object));
>     void _removeCallbacksFrom(Object object);
>     mixin SlotTracking;
> }
> 
> It's basically a replacement for multiple inheritance.
> 
> Would it be useful?  Any issues I've overlooked?
> 
> --bb

It is a very common idiom, and your proposal might have merit.  Except that -- at least in 
my personal practice, mind you -- the idea is to provide via the mixin only a "standard" 
or "minimal" implementation.  The path needs to stay open for class designers to do 
something unusual if the case warrants it.

That said, I do still like the spirit of the proposal: maybe if there were a way for the 
class designer to specify this behavior in the inheritance syntax, instead?  Alas, I can't 
immediately think of anything clean and concise.

-- Chris Nicholson-Sauls
October 28, 2006
Re: interface + mixin combination
Chris Nicholson-Sauls wrote:
> Bill Baxter wrote:
> 
>> Looking at Lutger's new sigslot code, it occurred to me that this is 
>> probably a pretty common (and useful) combination:
>>
>>> class Receiver : ISlotTracking
>>> {
>>>     void handleClick() { writefln("button clicked"); }
>>>     mixin SlotTracking;
>>> }
>>
>>
>>
>> I.e. using both a) derivation from an interface and b) a mixin.
>>
>> I've seen similar C++ GUI libraries, where a macro takes the place of 
>> the mixin.   Examples I know of include
>>
>> Qt - derive from QObject and use the QOBJECT macro
>> wx - derive from wxObject and use the DECLARE_CLASS macro
>> FOX - derive from FXObject and use FXDECLARE macro
>>
>> It's a useful combo which is similar to regular class inheritance, but 
>> different in that the implementation/behavior is defined locally and 
>> non-virtually rather than being inherited from the base class.
>>
>> The obvious syntax for it would be to just allow 'mixin' to appear in 
>> an interface.
>>
>> I.e. for Lutger's code:
>> interface ISlotTracking
>> {
>>     void _registerCallback(void delegate(Object));
>>     void _removeCallbacksFrom(Object object);
>>     mixin SlotTracking;
>> }
>>
>> It's basically a replacement for multiple inheritance.
>>
>> Would it be useful?  Any issues I've overlooked?
>>
>> --bb
> 
> 
> It is a very common idiom, and your proposal might have merit.  Except 
> that -- at least in my personal practice, mind you -- the idea is to 
> provide via the mixin only a "standard" or "minimal" implementation.  
> The path needs to stay open for class designers to do something unusual 
> if the case warrants it.
> 
> That said, I do still like the spirit of the proposal: maybe if there 
> were a way for the class designer to specify this behavior in the 
> inheritance syntax, instead?  Alas, I can't immediately think of 
> anything clean and concise.
> 
> -- Chris Nicholson-Sauls


Yeh, I was thinking that too.  Having both a with mixin and without 
mixin version seems frequently necessary.

Maybe just use a superinterface?

interface ISlotTracking
{
    // just the interface part
    void _registerCallback(void delegate(Object));
    void _removeCallbacksFrom(Object object);
}

interface MSlotTracking : ISlotTracking
{
    // ISlotTracking interface + implementation mixin
    mixin SlotTracking;
}

This is a pretty minor thing in the grand scheme of things, though. 
Definitely post 1.0.  C++ has limped along fine with the inherit class + 
macro implementation trick.

--bb
October 30, 2006
Re: interface + mixin combination
How about doing it around the other way?

> template SlotTracking
> {
>     implements ISlotTracking;
>     // implementation...
> }
>
> class Reciever
> {
>     void handleClick() { writefln("in ad2101 button clicked!"); }
>     mixin SlotTracking;
> }

After all, you might want to implement the interface without pulling in
the mixin, but using the mixin doesn't make much sense *without*
implementing the interface.

Another syntax idea while I'm at it:

> template SlotTracking : ISlotTracking
> {
>     // implementation...
> }

	-- Daniel

-- 
Unlike Knuth, I have neither proven or tried the above; it may not even
make sense.

v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D
i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP  http://hackerkey.com/
October 31, 2006
Re: interface + mixin combination
Daniel Keep wrote:
> How about doing it around the other way?
> 
>> template SlotTracking
>> {
>>     implements ISlotTracking;
>>     // implementation...
>> }
>>
>> class Reciever
>> {
>>     void handleClick() { writefln("in ad2101 button clicked!"); }
>>     mixin SlotTracking;
>> }
> 
> After all, you might want to implement the interface without pulling in
> the mixin, but using the mixin doesn't make much sense *without*
> implementing the interface.
> 
> Another syntax idea while I'm at it:
> 
>> template SlotTracking : ISlotTracking
>> {
>>     // implementation...
>> }
> 
> 	-- Daniel
> 

Hmm. I like seeing the clear indicator in the class implementing the 
interface that it can be treated as something other than just it's base 
class type.  So maybe there could be a special 'mixin' inheritance.

class Receiver : mixin ISlotTracking
  // interface *and* mixin implementation taken
{
   ...
}

vs

class Receiver : ISlotTracking
// just the interface part taken, I'll handle the implementation myself
{
  ...
}

--bb
October 31, 2006
Re: signal slots (new, fixed version)
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
October 31, 2006
Re: signal slots (new, fixed version)
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.
November 01, 2006
Re: signal slots (new, fixed version)
On Tue, 31 Oct 2006 05:41:52 +0200, Lutger <lutger.blijdestijn@gmail.com>  
wrote:
> Bill Baxter wrote:
[snip]
>> 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'
    };
November 01, 2006
Re: signal slots (new, fixed version)
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
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home