www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Only one signal per object?

reply Peter Ravinovich <PeterRavinovich thisisnotmyrealemail.com> writes:
Is there a way to have several signals per object?

The example in std.signal seams to suggest that only one signal per object can
be emmited. Is it possible to have several events launched as it's possible in
.NET?

For example, bind one object to onClick and another to onKeyUp.

Thanks
Oct 11 2011
next sibling parent reply Justin Whear <justin economicmodeling.com> writes:
From the docs:
"Different signals can be added to a class by naming the mixins."

So I think something like this ought to work:
mixin Signal!(string) onBlah;
mixin Signal!(int, int) onClicketyClick;


Peter Ravinovich wrote:

 Is there a way to have several signals per object?
 
 The example in std.signal seams to suggest that only one signal per object
 can be emmited. Is it possible to have several events launched as it's
 possible in .NET?
 
 For example, bind one object to onClick and another to onKeyUp.
 
 Thanks

Oct 11 2011
next sibling parent reply Spacen Jasset <spacenjasset yahoo.co.uk> writes:
On 11/10/2011 18:12, Justin Whear wrote:
  From the docs:
 "Different signals can be added to a class by naming the mixins."

 So I think something like this ought to work:
 mixin Signal!(string) onBlah;
 mixin Signal!(int, int) onClicketyClick;


 Peter Ravinovich wrote:

 Is there a way to have several signals per object?

 The example in std.signal seams to suggest that only one signal per object
 can be emmited. Is it possible to have several events launched as it's
 possible in .NET?

 For example, bind one object to onClick and another to onKeyUp.

 Thanks


Perhaps it should be that the signals must be explicit to make things clear cut.
Oct 12 2011
parent Justin Whear <justin economicmodeling.com> writes:
Yes, the docs don't elaborate or include an example. So, for posterity's 
sake here's a quick example:

----------------------------------------
import std.stdio,
        std.signals;

void main()
{
        auto foo = new Foo;
        auto listener = new Listener;

        foo.onClick.connect(&listener.clickHandler);
        foo.onFoo.connect(&listener.fooHandler);

        foo.doClick(10, 10);
        foo.doFoo("a message");
}

class Foo 
{
        void doClick(int x, int y) { onClick.emit(x, y); }
        void doFoo(string s) { onFoo.emit(s); }

        mixin Signal!(int,int) onClick;
        mixin Signal!(string) onFoo;
} 

class Listener
{
        void clickHandler(int x, int y)
        {
        	writeln("Clicked at ", x, ",", y);
        }

        void fooHandler(string bar)
        {
        	writeln("Got ", bar);
        }
}
----------------------------------------

$ rdmd test.d
Clicked at 10,10
Got a message


Spacen Jasset wrote:

 On 11/10/2011 18:12, Justin Whear wrote:
  From the docs:
 "Different signals can be added to a class by naming the mixins."

 So I think something like this ought to work:
 mixin Signal!(string) onBlah;
 mixin Signal!(int, int) onClicketyClick;


 Peter Ravinovich wrote:

 Is there a way to have several signals per object?

 The example in std.signal seams to suggest that only one signal per
 object can be emmited. Is it possible to have several events launched as
 it's possible in .NET?

 For example, bind one object to onClick and another to onKeyUp.

 Thanks


Perhaps it should be that the signals must be explicit to make things clear cut.

Oct 12 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Johannes Pfau made an updated version of std.signals, I think he's
hosting it somewhere on his dropbox, you'll have to ask him.
Oct 12 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I've ran into an awful bug right now (probably not related to your
module). It basically comes down to this inside of a "Widget"
constructor:

    this(Widget parent)
    {
        super(parent);

        void test()
        {
            msgbox(this.position);
        }

        parent.onMouseLDown.connect( { this.position;  });
        parent.onMouseLDown.connect( { test(); });
    }

If I comment out the first connect call I'll get an access violation
when 'test()' tries to access the "position" field. The order of the
two calls doesn't seem to affect the bug. I can't recreate the bug in
a simple test-case though.. :/

Oh btw, there's a couple of failing unittests in your signal module
when compiling with " -g  -debug -w -wi":

object.Exception signalsnew.d(679): Handler is not connected
core.exception.AssertError signalsnew(950): unittest failure
core.exception.AssertError signalsnew(968): unittest failure
Oct 18 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
It seems neither std.signal nor the new signals module support ref
parameters, which is a shame because I've found a very good use for
them.

The std.signal won't compile for handlers with ref parameters, the new
one compiles but throws an access violation at runtime.

Example usage: If you have a child widget and its parent widget which
is a Layout widget, when the child emits a "doMove(Widget widget,
Point newPoint)" signal the layout widget could handle it first by
calling connectFirst when the parenting takes place. This allows the
layout 'doMove' handler to modify the parameter so it fits some given
limit, after which the next handler is called to further process the
signal.

In code this would look something like:
auto button = new Widget;
auto layout = new Layout(...);
layout.addWidget(layout);
// internally layout would do: widget.doMove.connectFirst(&layout.doMove);
widget.move(Point(10, 10));

The last call translates to, say:
-> widget.doMove.emit(this, Point(10, 10));

Which calls all the handlers sequentially until one of them returns 0:
-> widget.doMove.handler[0]  // layout.doMove()
-> widget.doMove.handler[1]  // handler 2..

and in Layout:
bool doMove(Widget widget, ref Point newPoint)
{
    newPoint.x = max(newPoint.x, 10);  // limit to 10
    newPoint.y = max(newPoint.y, 10);
    return true;
}

and then the other handlers would be called, but with potentially
modified arguments. This would be super-useful to me right now. I
haven't yet gone through the new signal implementation, but maybe I'll
figure out how to add this feature myself.
Oct 24 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I think I know what's going on,

getCallable() doesn't check for the storage class of a function, and
does a cast based on the parameters and the return type when it's
adding a new handler. The handler is later casted to a function type
with no ref parameters, and this is where things probably cause
problems.

I'll have to devise something more sophisticated.
Oct 24 2011
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Yeah, if I change the two function types in Signal to this:
        alias bool delegate(ref Types) slot_t;
        alias void delegate(ref Types) void_slot_t;

then I can connect functions with ref arguments. But then I'll get
exceptions thrown if the functions have non-ref arguments.

This seems like a difficult problem to solve. How can you
(efficiently) store a dynamic list of function pointers which could
have arbitrary storage classes for its parameters? Hmm..
Oct 24 2011
prev sibling parent Johannes Pfau <spam example.com> writes:
Andrej Mitrovic wrote:
Johannes Pfau made an updated version of std.signals, I think he's
hosting it somewhere on his dropbox, you'll have to ask him.

http://dl.dropbox.com/u/24218791/d/src/signals.html https://gist.github.com/1194497 It should work well as a replacement for the current std.signals but compared to boost::signals2 it still misses some features. When I find some time for it, I'll update it to support all boost:signals2 features and propose it for phobos. Note: This bug can cause issues with my std.signals implementation: http://d.puremagic.com/issues/show_bug.cgi?id=5011 I should really file a pull request for that. -- Johannes Pfau
Oct 14 2011