Jump to page: 1 2
Thread overview
signal slots (new, fixed version)
Oct 27, 2006
Lutger
interface + mixin combination
Oct 28, 2006
Bill Baxter
Oct 28, 2006
Bill Baxter
Oct 30, 2006
Daniel Keep
Oct 31, 2006
Bill Baxter
Oct 31, 2006
Bill Baxter
Oct 31, 2006
Lutger
Nov 01, 2006
Kristian
Nov 01, 2006
Thomas Kuehne
Nov 01, 2006
Lutger
Nov 01, 2006
Sean Kelly
Nov 01, 2006
Walter Bright
Nov 01, 2006
J Duncan
Nov 01, 2006
Walter Bright
Nov 01, 2006
Sean Kelly
Nov 01, 2006
Lutger
Nov 02, 2006
Bill Baxter
Nov 02, 2006
Walter Bright
October 27, 2006
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
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
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
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
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
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
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
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
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
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