Suggestion: signal/slot mechanism
Kristian
kjkilpi at gmail.com
Thu Sep 7 00:07:38 PDT 2006
On Wed, 06 Sep 2006 03:50:02 +0300, Lutger <lutger.blijdestijn at gmail.com>
wrote:
> Kristian wrote:
> <snip>
>> Bruno Medeiros wrote:
>>
>>> I see, so a S/S object also knows which signals point to his slots.
>>> Still, a base class is not required, a mixin can do the job nearly as
>>> well.
>>>
>>>> I think everybody will agree if I say that the optimal solution would
>>>> be D supporting S/S mechanism directly.
>>>>
>>>
>>> I disagree. If D can support S/S without additional languages
>>> constructs, and with nearly the same easy of use as Qt's, then that's
>>> enough.
>> Well, I think the key sentence here is "almost as easy as". Will it
>> be easy enough for the majority?
>> The S/S mechanism is a very simple structure. If you build it 'right',
>> then there is no room for competition (because you cannot make it
>> simplier). And being simple, it won't bloat the language. Instead it'll
>> extend the language 'naturally'.
>> Mixins and templates would make the mechanism quite complex. That
>> would bloat the *code*.
>
> Of course you are right that built-in S/S can be cleaner (and more
> efficient), IIRC C#'s events are something like it. But it also has some
> disadvantages, for example boost::signals is quite different than QT's,
> and the latter also has features for introspection. These libraries (as
> a whole) are higher-level than is reasonable for D to incorporate into
> the language.
>
> I'm also not saying that dcouple is poorly written, but I think it can
> be done a little clearer. This thread inspired me to work on the managed
> part of my sigslot module, below I've added how your example looks like
> in it. It also handles other callable types (like free functions). There
> is no need to derive from any class or interface.
>
> class Foo {
> void setValue(int value) {
> ...
> valueChanged1();
> valueChanged2(oldVal, value);
> }
>
> Signal!() valueChanged1;
> Signal!(void, int, int) valueChanged2;
> }
>
> class Bar {
> this() {
> foo = new Foo;
> foo.valueChanged1.connect(handleFoo(), this);
>
> foo2 = new Foo;
> foo2.valueChanged2.connect(handleFoo(), this);
> }
>
> void handleFoo() {...}
> void handleFoo(int oldValue, int newValue) {...}
>
> Foo foo, foo2;
>
> mixin SlotObjectDestructor;
> }
Well now, this is almost like having a direct support! :)
One have to use a mixin and the signal names cannot be overloaded.
However, these are not big drawbacks at all. Actually the overload thing
is a benefit when connecting: you don't have to define parameter types. I
like the syntax very much indeed; this is a must for Phobos...! ;)
At first I thought that I would like the 'connect()' function to have
parameters swapped (e.g. "foo.valueChanged1.connect(this, handleFoo());"),
but now I think the current order is better.
BTW, I'm wondering why there is 'void' in "Signal!(void, int, int)"? Is it
intentional?
>
>> For example:
>> /* ----- Qt-styled way with some modifications */
>> class Foo {
>> void setValue(int value) {
>> ...
>> emit valueChanged();
>> emit valueChanged(oldVal, value);
>> }
>> signal valueChanged();
>> signal valueChanged(int oldValue, int newValue);
>> }
>> class Bar {
>> this() {
>> foo = new Foo;
>> connect foo.ValueChanged() to handleFoo();
>> foo2 = new Foo;
>> connect foo2.valueChanged(int, int) to handleFoo(int, int);
>> }
>> void handleFoo() {...}
>> void handleFoo(int oldValue, int newValue) {...}
>> Foo foo, foo2;
>> }
>> /* ----- dcouple-styled way */
>> // The Widget class implements the S/S handling.
>> // It's derived from dcouple's SignalSlotManager.
>> // Its implementation is omited here.
>> class Foo : Widget {
>> this() {
>> sigValueChanged1 = new Signal!()(this);
>> sigValueChanged2 = new Signal!(int, int)(this);
>> }
>> void setValue(int value) {
>> ...
>> sigValueChanged1.emit();
>> sigValueChanged2.emit(oldVal, value);
>> }
>> Signal!() sigValueChanged1;
>> Signal!(int, int) sigValueChanged2;
>> }
>> class Bar : Widget {
>> this() {
>> foo = new Foo;
>> slotHandleFoo1 = new Slot!()(this, &handleFoo1);
>> connect(foo.sigValueChanged1, slotHandleFoo1);
>> foo2 = new Foo;
>> slotHandleFoo2 = new Slot!(int, int)(this, &handleFoo2);
>> connect(foo2.sigValueChanged2, slotHandleFoo2);
>> }
>> void handleFoo1() {...}
>> void handleFoo2(int oldValue, int newValue) {...}
>> Slot!() slotHandleFoo1;
>> Slot!(int, int) slotHandleFoo2;
>> Foo foo, foo2;
>> }
>> I am not saying that dcouple is poorly written or anything (far from
>> it!), but the benefits of the direct support is obvious IMHO:
>> - Less code (and less potential bugs).
>> - Clearer syntax which is easier to read/write.
>> - Signals can be connected to any object and function.
>> - No need to use a base class (or mixins).
>> - Function overloads can be used.
>> - Guaranteed to be bug free.
More information about the Digitalmars-d
mailing list