www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - std.signals with multiple signals?

reply Serg Kovrov <kovrov no.spam> writes:
Hello, D-folks!

First, my understanding of S&S:

Instance of 'emitter' class could emit predefined set of signals.
And arbitrary class could be a 'listener', e.g. connect any of those 
'signals' to a it's 'slot' (arbitrary method with particular signature).

And I expect something like this pseudo code:
connect emitter.clicked to listener.do_something
connect emitter.double_clicked to listener.do_something_else

With current std.signals 
(http://digitalmars.com/d/phobos/std_signals.html) I don't get how to 
choose signal to connect to particular slot. I mean, how to define more 
than one signal?

Meanwhile, before std.signals and all S&S buzz, I used followed 
(simplified, but you could see the idea) approach:

 class Emitter
 {
     enum EV {CLICKED, DBLCLICKED}
     void delegate()[EV] subscribers;
 
     void emit(EV ev) {
         auto fn = ev in subscribers;
         try { if (fn != null) (*fn)(); }
         catch (Exception wtf) { writefln("WTF? %s", wtf); }
     }
     void subscribe(EV ev, void delegate() fn) {
         subscribers[ev] = fn;
     }
     void click()    { emit(EV.CLICKED); }
     void dblclick() { emit(EV.DBLCLICKED); }
 }
 
 class Listener
 {
     void do_something()      { writefln("do_something"); }
     void do_something_else() { writefln("do_something_else"); }
 }
 
 void main()
 {
     auto emitter = new Emitter();
     auto listener = new Listener();
     emitter.subscribe(Emitter.EV.CLICKED,
 	  &listener.do_something);
     emitter.subscribe(Emitter.EV.DBLCLICKED,
 	  &listener.do_something_else);
     emitter.click();
     delete listener;
     emitter.dblclick();
 }

And it quite works for me, is there something wrong with it? -- serg.
Nov 15 2006
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Serg Kovrov wrote:
 Hello, D-folks!

Howdy.
 First, my understanding of S&S:
 
 Instance of 'emitter' class could emit predefined set of signals.

In Qt-style S&S, the signal is an object that knows how to do its own emitting, rather than just being some enum code and relying on an Emitter object. It's just a stylistic choice, but I think it's cleaner than having one emitter who knows about multiple kinds of signals.
 And arbitrary class could be a 'listener', e.g. connect any of those 
 'signals' to a it's 'slot' (arbitrary method with particular signature).

That part is the same with Qt / phobos sig&slot.
 And I expect something like this pseudo code:
 connect emitter.clicked to listener.do_something
 connect emitter.double_clicked to listener.do_something_else
 
 With current std.signals 
 (http://digitalmars.com/d/phobos/std_signals.html) I don't get how to 
 choose signal to connect to particular slot. I mean, how to define more 
 than one signal?

Like this: class MyClass { mixin Signal!() clicked; mixin Signal!() dblclicked; }; ... auto c = new MyClass; c.clicked.connect(&listener.do_something); c.dblclicked.connect(&listener.do_something_else); clicked.emit(); dblclicked.emit(); Or with the signalobj.d file I posted a few messages back you can do: auto clicked = new SignalObj!(); auto dlbclicked = new SignalObj!(); clicked.connect(&listener.do_something); dblclicked.connect(&listener.do_something_else); clicked.emit(); dblclicked.emit(); SignalObj is basically nothing more than: class(T...){ mixin Signal!(T); }
 Meanwhile, before std.signals and all S&S buzz, I used followed
 (simplified, but you could see the idea) approach:

 class Emitter
 {
    [snip]
 }

And it quite works for me, is there something wrong with it?

One thing that's missing from your simplified S&S is that your signal can't pass any extra information to the slots/listeners. Your emitter only accepts delegates with zero arguments. You could write a different Emitter that can pass some arguments, but every time you have a different set of arguments to pass you'd have to write a new Emitter. The std.signal.Signal mixin automates all that. mixin Signal!(Widget,double) value_changed; That one line of code basically creates an entire new customized version of your Emitter class tailored for passing a Widget and a double to listeners. Another difference is that you haven't provided anything for disconnecting a listener who's no longer interested, or made any provisions for a listener getting destroyed. All that is handled by std.signals. Furthermore if you use the FlexSignal class I just posted a few messages back you will gain additional capabilities like being able to connect this function: int set_value(int v) { writefln("The value is: ", v); } to this signal: FlexSignal!(int, char[]) sig; sig.emit(5, "A message") will call set_value(5) and ignore its return value. With SignalObj, or a std.signals 'mixin Signal', trying to connect these two will give you a variety of compile time errors, because a) it's a function and not a delegate, and because b) it has a return value, and because c) it doesn't take as many arguments as the Signal was expecting. But your basic approach (make an array of delegates) is the same as what std.signals uses. --bb
Nov 15 2006
next sibling parent reply Serg Kovrov <kovrov no.spam> writes:
Thanks for examples and explanations, Bill.

The key point I have overlooked in docs is "Different signals can be 
added to a class _by naming the mixins_". That clicked everything into 
place.

-- 
serg.
Nov 15 2006
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Serg Kovrov wrote:
 Thanks for examples and explanations, Bill.
 
 The key point I have overlooked in docs is "Different signals can be 
 added to a class _by naming the mixins_". That clicked everything into 
 place.
 

Yeh I missed that at first too. That was the first I learned you could name a mixin, too. :-) --bb
Nov 15 2006
prev sibling parent Ary Manzana <ary esperanto.org.ar> writes:
Serg Kovrov escribió:
 Thanks for examples and explanations, Bill.
 
 The key point I have overlooked in docs is "Different signals can be 
 added to a class _by naming the mixins_". That clicked everything into 
 place.
 

I missed that too the first say I saw it. Could the example in http://www.digitalmars.com/d/phobos/std_signals.html be changed to use named signals, and more than one in a class? Thanks, Ary
Nov 16 2006
prev sibling parent Serg Kovrov <kovrov no.spam> writes:
Bill Baxter wrote:
 Another difference is that you haven't provided anything for 
 disconnecting a listener who's no longer interested, or made any 
 provisions for a listener getting destroyed.  All that is handled by 
 std.signals.

True, I did not yet implement disconnecting, mostly because my Listeners usually lives longer than Subjects. In those rare cases I just catch an "Access Violation" exception, which probably not a best practice, though. -- serg.
Nov 16 2006