www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Suggestion: signal/slot mechanism

reply Kristian <kjkilpi gmail.com> writes:
It would be nice if D had a signal/slot mechanism similiar to Qt:
http://doc.trolltech.com/4.1/signalsandslots.html

It's an elegant way to handle messages sent between objects. It beats  
event table declarations used by other GUI libraries IMHO.

It would make D a lot more appealing language to write GUI applications.  
Think of wxWidgets written in D... ;)


I think it would be quite simple to build a S/S support for a compiler (at  
first glance, at least). For example:

The 'Object' class has a pointer to S/S data (it's null if the object  
don't currently use signals/slots). S/S data holds a slot list for each  
signal. It also holds a list of objects that have slot(s) connected to  
this object's signal(s). This list is used to disconnect necessary slots  
at a destruction of the object.

When the compiler reads a 'emit X' statement, it will do two things.  
First, it generates an id for the signal which is used to retrieve a  
correct slot list.

Second, the compiler puts the signal's parameters to the stack as it would  
call a corresponding function. Instead, the 'Object._emit_signal(id)'  
function (or something) is called, where 'id' is the generated id. (Note  
that there are no function bodies for signals.) '_emit_signal()' retrieves  
the correct slot list, and calls all the slots (delegates) in it. Finally  
the parameters are removed from the stack.

Of course, slots should not modify their parameters so that all the slots  
will receive the same parameter values. Hence slots should not use the  
'out type'. There is a market for a 'const type' here... *wink*


Maybe there should be no slot keyword at all as there is in Qt. You don't  
need to declare a function to be a slot; all the (virtual) functions can  
be used with signals.

Because the return values of all the signals are void, the void typeword  
could be removed from signal declarations.

signal clicked();

signals:
clicked();
clicked(int button);


BTW, Qt generates ids for signals as follows:

signals:
void clicked(int button, bool isMoved);
-> the id is a string "clicked(int,bool)"
Sep 02 2006
next sibling parent reply Lutger <lutger.blijdestijn gmail.com> writes:
Kristian wrote:
 It would be nice if D had a signal/slot mechanism similiar to Qt:
 http://doc.trolltech.com/4.1/signalsandslots.html
 
 It's an elegant way to handle messages sent between objects. It beats 
 event table declarations used by other GUI libraries IMHO.
 
 It would make D a lot more appealing language to write GUI applications. 
 Think of wxWidgets written in D... ;)
 
 
 I think it would be quite simple to build a S/S support for a compiler 
 (at first glance, at least). For example:
 
 The 'Object' class has a pointer to S/S data (it's null if the object 
 don't currently use signals/slots). S/S data holds a slot list for each 
 signal. It also holds a list of objects that have slot(s) connected to 
 this object's signal(s). This list is used to disconnect necessary slots 
 at a destruction of the object.
 
 When the compiler reads a 'emit X' statement, it will do two things. 
 First, it generates an id for the signal which is used to retrieve a 
 correct slot list.
 
 Second, the compiler puts the signal's parameters to the stack as it 
 would call a corresponding function. Instead, the 
 'Object._emit_signal(id)' function (or something) is called, where 'id' 
 is the generated id. (Note that there are no function bodies for 
 signals.) '_emit_signal()' retrieves the correct slot list, and calls 
 all the slots (delegates) in it. Finally the parameters are removed from 
 the stack.
 
 Of course, slots should not modify their parameters so that all the 
 slots will receive the same parameter values. Hence slots should not use 
 the 'out type'. There is a market for a 'const type' here... *wink*
 
 
 Maybe there should be no slot keyword at all as there is in Qt. You 
 don't need to declare a function to be a slot; all the (virtual) 
 functions can be used with signals.
 
 Because the return values of all the signals are void, the void typeword 
 could be removed from signal declarations.
 
 signal clicked();
 
 signals:
 clicked();
 clicked(int button);
 
 
 BTW, Qt generates ids for signals as follows:
 
 signals:
 void clicked(int button, bool isMoved);
 -> the id is a string "clicked(int,bool)"
I agree the signal-slot mechanism is nice, but why must it be supported by a compiler? There a lots of good libraries in C++ for this, such as sigslots, libsigc++ and boost::signals. It is not necessary to have extra preprocessor or language support. It should be possible to do this in a D library. There already is dcouple at dsource, but it is inactive. I have been working on a signal-slot module myself, the only thing missing now is automatic disconnections of which I am not sure how and if to proceed.
Sep 02 2006
parent Kristian <kjkilpi gmail.com> writes:
On Sat, 02 Sep 2006 14:20:22 +0300, Lutger <lutger.blijdestijn gmail.com>  
wrote:
 Kristian wrote:
 It would be nice if D had a signal/slot mechanism similiar to Qt:
 http://doc.trolltech.com/4.1/signalsandslots.html
  It's an elegant way to handle messages sent between objects. It beats  
 event table declarations used by other GUI libraries IMHO.
  It would make D a lot more appealing language to write GUI  
 applications. Think of wxWidgets written in D... ;)
[snip]
 I agree the signal-slot mechanism is nice, but why must it be supported  
 by a compiler? There a lots of good libraries in C++ for this, such as  
 sigslots, libsigc++ and boost::signals. It is not necessary to have  
 extra preprocessor or language support.

 It should be possible to do this in a D library. There already is  
 dcouple at dsource, but it is inactive. I have been working on a  
 signal-slot module myself, the only thing missing now is automatic  
 disconnections of which I am not sure how and if to proceed.
Well, one could ask if it's possible to implement a signal/slot mechanism by using a library (or by doing it by yourself), then why none of the GUI libraries I'm familiar with do not use it? (Qt uses preprocessor.) There are several general reasons for a feature being directly supported: - If a feature is supported by a language, programmers will use it a lot more often. Actually it almost certainly guarantees that a feature will be used. - Syntax sugar. Being the part of a language, the syntax is consistent with the rest of the statements. This makes the feature (usually) simplier and easier to use. At least overall readability is better. - Compile time errors could be more informative. - If there is a compiler for a operating system, then the feature is automatically supported in that system, of course. - You can rely on the feature being bug free. (You can make that assumption.) And of course, the S/S mechanism wouldn't look bad at the feature list of D. ;)
Sep 02 2006
prev sibling next sibling parent reply Brad Anderson <brad dsource.org> writes:
Kristian wrote:
 It would be nice if D had a signal/slot mechanism similiar to Qt:
 http://doc.trolltech.com/4.1/signalsandslots.html
 
 It's an elegant way to handle messages sent between objects. It beats
 event table declarations used by other GUI libraries IMHO.
 
 It would make D a lot more appealing language to write GUI applications.
 Think of wxWidgets written in D... ;)
 
 
 I think it would be quite simple to build a S/S support for a compiler
 (at first glance, at least). For example:
 
 The 'Object' class has a pointer to S/S data (it's null if the object
 don't currently use signals/slots). S/S data holds a slot list for each
 signal. It also holds a list of objects that have slot(s) connected to
 this object's signal(s). This list is used to disconnect necessary slots
 at a destruction of the object.
 
 When the compiler reads a 'emit X' statement, it will do two things.
 First, it generates an id for the signal which is used to retrieve a
 correct slot list.
 
 Second, the compiler puts the signal's parameters to the stack as it
 would call a corresponding function. Instead, the
 'Object._emit_signal(id)' function (or something) is called, where 'id'
 is the generated id. (Note that there are no function bodies for
 signals.) '_emit_signal()' retrieves the correct slot list, and calls
 all the slots (delegates) in it. Finally the parameters are removed from
 the stack.
 
 Of course, slots should not modify their parameters so that all the
 slots will receive the same parameter values. Hence slots should not use
 the 'out type'. There is a market for a 'const type' here... *wink*
 
 
 Maybe there should be no slot keyword at all as there is in Qt. You
 don't need to declare a function to be a slot; all the (virtual)
 functions can be used with signals.
 
 Because the return values of all the signals are void, the void typeword
 could be removed from signal declarations.
 
 signal clicked();
 
 signals:
 clicked();
 clicked(int button);
 
 
 BTW, Qt generates ids for signals as follows:
 
 signals:
 void clicked(int button, bool isMoved);
 -> the id is a string "clicked(int,bool)"
Not terribly active recently, but the SVN repos here may have some stuff for you. http://svn.dsource.org/projects/dcouple/ BA
Sep 02 2006
parent Kristian <kjkilpi gmail.com> writes:
On Sun, 03 Sep 2006 01:41:55 +0300, Brad Anderson <brad dsource.org> wrote:
 Kristian wrote:
 It would be nice if D had a signal/slot mechanism similiar to Qt:
 http://doc.trolltech.com/4.1/signalsandslots.html

 It's an elegant way to handle messages sent between objects. It beats
 event table declarations used by other GUI libraries IMHO.

 It would make D a lot more appealing language to write GUI applications.
 Think of wxWidgets written in D... ;)


 I think it would be quite simple to build a S/S support for a compiler
 (at first glance, at least). For example:

 The 'Object' class has a pointer to S/S data (it's null if the object
 don't currently use signals/slots). S/S data holds a slot list for each
 signal. It also holds a list of objects that have slot(s) connected to
 this object's signal(s). This list is used to disconnect necessary slots
 at a destruction of the object.

 When the compiler reads a 'emit X' statement, it will do two things.
 First, it generates an id for the signal which is used to retrieve a
 correct slot list.

 Second, the compiler puts the signal's parameters to the stack as it
 would call a corresponding function. Instead, the
 'Object._emit_signal(id)' function (or something) is called, where 'id'
 is the generated id. (Note that there are no function bodies for
 signals.) '_emit_signal()' retrieves the correct slot list, and calls
 all the slots (delegates) in it. Finally the parameters are removed from
 the stack.

 Of course, slots should not modify their parameters so that all the
 slots will receive the same parameter values. Hence slots should not use
 the 'out type'. There is a market for a 'const type' here... *wink*


 Maybe there should be no slot keyword at all as there is in Qt. You
 don't need to declare a function to be a slot; all the (virtual)
 functions can be used with signals.

 Because the return values of all the signals are void, the void typeword
 could be removed from signal declarations.

 signal clicked();

 signals:
 clicked();
 clicked(int button);


 BTW, Qt generates ids for signals as follows:

 signals:
 void clicked(int button, bool isMoved);
 -> the id is a string "clicked(int,bool)"
Not terribly active recently, but the SVN repos here may have some stuff for you. http://svn.dsource.org/projects/dcouple/ BA
Thanks. I like dcouple's syntax. (There seem to be some problems with GC and object deletion currently though.) Being a library, you have to create signal and slot objects in constructors, which is a downside (when compared against S/S mechanism build into D).
Sep 03 2006
prev sibling next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Kristian wrote:
 It would be nice if D had a signal/slot mechanism similiar to Qt:
 http://doc.trolltech.com/4.1/signalsandslots.html
 
 It's an elegant way to handle messages sent between objects. It beats 
 event table declarations used by other GUI libraries IMHO.
 
 It would make D a lot more appealing language to write GUI applications. 
 Think of wxWidgets written in D... ;)
 
 
 I think it would be quite simple to build a S/S support for a compiler 
 (at first glance, at least). For example:
 
 The 'Object' class has a pointer to S/S data (it's null if the object 
 don't currently use signals/slots). S/S data holds a slot list for each 
 signal. It also holds a list of objects that have slot(s) connected to 
 this object's signal(s). This list is used to disconnect necessary slots 
 at a destruction of the object.
 
 When the compiler reads a 'emit X' statement, it will do two things. 
 First, it generates an id for the signal which is used to retrieve a 
 correct slot list.
 
 Second, the compiler puts the signal's parameters to the stack as it 
 would call a corresponding function. Instead, the 
 'Object._emit_signal(id)' function (or something) is called, where 'id' 
 is the generated id. (Note that there are no function bodies for 
 signals.) '_emit_signal()' retrieves the correct slot list, and calls 
 all the slots (delegates) in it. Finally the parameters are removed from 
 the stack.
 
 Of course, slots should not modify their parameters so that all the 
 slots will receive the same parameter values. Hence slots should not use 
 the 'out type'. There is a market for a 'const type' here... *wink*
 
 
 Maybe there should be no slot keyword at all as there is in Qt. You 
 don't need to declare a function to be a slot; all the (virtual) 
 functions can be used with signals.
 
 Because the return values of all the signals are void, the void typeword 
 could be removed from signal declarations.
 
 signal clicked();
 
 signals:
 clicked();
 clicked(int button);
 
 
 BTW, Qt generates ids for signals as follows:
 
 signals:
 void clicked(int button, bool isMoved);
 -> the id is a string "clicked(int,bool)"
The Signal and slots pattern is little more than an abstraction for languages that do not support delegates (and dynamic arrays). Which is not the case for D: // Declare a signal: void delegate(Button, int)[] someSignal; // Connect a slot to a signal: someSignal ~= foo.someSlot; // emit the signal foreach(dg; someSignal) dg(myButton, myInt); The only limitation I see with D so far, is on the emit part. You can't create an emit function that works like this: emit(someSignal, myButton, myInt); and that would do the same as that foreach. Because you cannot do "parameterized"(whether compile time or runtime) function calls. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 03 2006
next sibling parent Juan Jose Comellas <jcomellas gmail.com> writes:
Bruno Medeiros wrote:
 The Signal and slots pattern is little more than an abstraction for
 languages that do not support delegates (and dynamic arrays). Which is
 not the case for D:
 
    // Declare a signal:
    void delegate(Button, int)[] someSignal;
 
    // Connect a slot to a signal:
    someSignal ~= foo.someSlot;
 
    // emit the signal
    foreach(dg; someSignal)
      dg(myButton, myInt);
 
 The only limitation I see with D so far, is on the emit part. You can't
 create an emit function that works like this:
    emit(someSignal, myButton, myInt);
 and that would do the same as that foreach. Because you cannot do
 "parameterized"(whether compile time or runtime) function calls.
 
This problem is usually solved by creating a template signal class per number of parameters that it can allow (i.e. the arity of the signal). You'd have something like: template Signal0() { ... } template Signal1(T1) { ... } template Signal2(T1, T2) { ... } template Signal3(T1, T2, T3) { ... } ... This is the solution used by libsigc++, boost, Qt Jambi, etc.
Sep 03 2006
prev sibling parent reply Kristian <kjkilpi gmail.com> writes:
On Sun, 03 Sep 2006 13:01:28 +0300, Bruno Medeiros  =

<brunodomedeiros+spam com.gmail> wrote:
 Kristian wrote:
 It would be nice if D had a signal/slot mechanism similiar to Qt:
 http://doc.trolltech.com/4.1/signalsandslots.html
  It's an elegant way to handle messages sent between objects. It beat=
s =
 event table declarations used by other GUI libraries IMHO.
  It would make D a lot more appealing language to write GUI  =
 applications. Think of wxWidgets written in D... ;)
   I think it would be quite simple to build a S/S support for a  =
 compiler (at first glance, at least). For example:
  The 'Object' class has a pointer to S/S data (it's null if the objec=
t =
 don't currently use signals/slots). S/S data holds a slot list for ea=
ch =
 signal. It also holds a list of objects that have slot(s) connected t=
o =
 this object's signal(s). This list is used to disconnect necessary  =
 slots at a destruction of the object.
  When the compiler reads a 'emit X' statement, it will do two things.=
=
 First, it generates an id for the signal which is used to retrieve a =
=
 correct slot list.
  Second, the compiler puts the signal's parameters to the stack as it=
=
 would call a corresponding function. Instead, the  =
 'Object._emit_signal(id)' function (or something) is called, where 'i=
d' =
 is the generated id. (Note that there are no function bodies for  =
 signals.) '_emit_signal()' retrieves the correct slot list, and calls=
=
 all the slots (delegates) in it. Finally the parameters are removed  =
 from the stack.
  Of course, slots should not modify their parameters so that all the =
=
 slots will receive the same parameter values. Hence slots should not =
=
 use the 'out type'. There is a market for a 'const type' here... *win=
k*
   Maybe there should be no slot keyword at all as there is in Qt. You=
=
 don't need to declare a function to be a slot; all the (virtual)  =
 functions can be used with signals.
  Because the return values of all the signals are void, the void  =
 typeword could be removed from signal declarations.
  signal clicked();
  signals:
 clicked();
 clicked(int button);
   BTW, Qt generates ids for signals as follows:
  signals:
 void clicked(int button, bool isMoved);
 -> the id is a string "clicked(int,bool)"
The Signal and slots pattern is little more than an abstraction for =
 languages that do not support delegates (and dynamic arrays). Which is=
=
 not the case for D:

    // Declare a signal:
    void delegate(Button, int)[] someSignal;

    // Connect a slot to a signal:
    someSignal ~=3D foo.someSlot;

    // emit the signal
    foreach(dg; someSignal)
      dg(myButton, myInt);

 The only limitation I see with D so far, is on the emit part. You can'=
t =
 create an emit function that works like this:
    emit(someSignal, myButton, myInt);
 and that would do the same as that foreach. Because you cannot do  =
 "parameterized"(whether compile time or runtime) function calls.
Automatic S/S disconnection at object destructions should also be = implemented, usually, which requires the use of a base class. I think everybody will agree if I say that the optimal solution would be= D = supporting S/S mechanism directly. It would make using of signals/slots as easy as calling of functions, fo= r = instance. But if Walter (and D community) thinks that the S/S mechanism = is = not important enough to be added to D specs (ever), then Phobos should = have a S/S library (in the future). It would make implementation of = classes having signals/slots more tedious though. Fortunately using of = such classes would be as easy (I looked at some examples of dcouple).
Sep 03 2006
next sibling parent Lutger <lutger.blijdestijn gmail.com> writes:
Kristian wrote:
<snip>
 
 Automatic S/S disconnection at object destructions should also be 
 implemented, usually, which requires the use of a base class.
 
 I think everybody will agree if I say that the optimal solution would be 
 D supporting S/S mechanism directly.
 
 It would make using of signals/slots as easy as calling of functions, 
 for instance. But if Walter (and D community) thinks that the S/S 
 mechanism is not important enough to be added to D specs (ever), then 
 Phobos should have a S/S library (in the future). It would make 
 implementation of classes having signals/slots more tedious though. 
 Fortunately using of such classes would be as easy (I looked at some 
 examples of dcouple).
I think there are benefits / downsides to both options of language and library implementation. Personally I favor a library option, and if it would be included in some (de facto or official) standard library that would be good indeed. In C++ the existing library solutions are very nice and prove that a preprocessor like QT uses is not needed at all. I think it can be done in D even better, because of delegate support and template features. The benefit of a library solution is less language bloat, more flexible and possible competition. Some libraries also perhaps don't want to use the signal-slot mechanism, like harmonia which prefers to use sinking-bubbling. It's not that it's not important enough, it's more that it fits better in a library for a language like D imho.
Sep 03 2006
prev sibling next sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Kristian wrote:
 On Sun, 03 Sep 2006 13:01:28 +0300, Bruno Medeiros 
 <brunodomedeiros+spam com.gmail> wrote:
 Kristian wrote:
 It would be nice if D had a signal/slot mechanism similiar to Qt:
 http://doc.trolltech.com/4.1/signalsandslots.html
  It's an elegant way to handle messages sent between objects. It 
 beats event table declarations used by other GUI libraries IMHO.
  It would make D a lot more appealing language to write GUI 
 applications. Think of wxWidgets written in D... ;)
   I think it would be quite simple to build a S/S support for a 
 compiler (at first glance, at least). For example:
  The 'Object' class has a pointer to S/S data (it's null if the 
 object don't currently use signals/slots). S/S data holds a slot list 
 for each signal. It also holds a list of objects that have slot(s) 
 connected to this object's signal(s). This list is used to disconnect 
 necessary slots at a destruction of the object.
  When the compiler reads a 'emit X' statement, it will do two things. 
 First, it generates an id for the signal which is used to retrieve a 
 correct slot list.
  Second, the compiler puts the signal's parameters to the stack as it 
 would call a corresponding function. Instead, the 
 'Object._emit_signal(id)' function (or something) is called, where 
 'id' is the generated id. (Note that there are no function bodies for 
 signals.) '_emit_signal()' retrieves the correct slot list, and calls 
 all the slots (delegates) in it. Finally the parameters are removed 
 from the stack.
  Of course, slots should not modify their parameters so that all the 
 slots will receive the same parameter values. Hence slots should not 
 use the 'out type'. There is a market for a 'const type' here... *wink*
   Maybe there should be no slot keyword at all as there is in Qt. You 
 don't need to declare a function to be a slot; all the (virtual) 
 functions can be used with signals.
  Because the return values of all the signals are void, the void 
 typeword could be removed from signal declarations.
  signal clicked();
  signals:
 clicked();
 clicked(int button);
   BTW, Qt generates ids for signals as follows:
  signals:
 void clicked(int button, bool isMoved);
 -> the id is a string "clicked(int,bool)"
The Signal and slots pattern is little more than an abstraction for languages that do not support delegates (and dynamic arrays). Which is not the case for D: // Declare a signal: void delegate(Button, int)[] someSignal; // Connect a slot to a signal: someSignal ~= foo.someSlot; // emit the signal foreach(dg; someSignal) dg(myButton, myInt); The only limitation I see with D so far, is on the emit part. You can't create an emit function that works like this: emit(someSignal, myButton, myInt); and that would do the same as that foreach. Because you cannot do "parameterized"(whether compile time or runtime) function calls.
Automatic S/S disconnection at object destructions should also be implemented, usually, which requires the use of a base class.
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.
 It would make using of signals/slots as easy as calling of functions, 
 for instance. But if Walter (and D community) thinks that the S/S 
With a library, in D, S/S can be used as easy as calling functions. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Sep 04 2006
prev sibling parent reply Kristian <kjkilpi gmail.com> writes:
On Sun, 03 Sep 2006 20:08:36 +0300, Kristian <kjkilpi gmail.com> wrote:

 On Sun, 03 Sep 2006 13:01:28 +0300, Bruno Medeiros  =
 <brunodomedeiros+spam com.gmail> wrote:
 Kristian wrote:
 It would be nice if D had a signal/slot mechanism similiar to Qt:
 http://doc.trolltech.com/4.1/signalsandslots.html
  It's an elegant way to handle messages sent between objects. It bea=
ts [snip] Ludger wrote:
 I think there are benefits / downsides to both options of language and=
=
 library implementation. Personally I favor a library option, and if it=
=
 would be included in some (de facto or official) standard library that=
=
 would be good indeed.

  In C++ the existing library solutions are very nice and prove that a =
=
 preprocessor like QT uses is not needed at all. I think it can be done=
=
 in D even better, because of delegate support and template features.

  The benefit of a library solution is less language bloat, more  =
 flexible   and possible competition. Some libraries also perhaps don't=
=
 want to use the signal-slot mechanism, like harmonia which prefers to =
=
 use sinking-bubbling.
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*. 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 =3D new Foo; connect foo.ValueChanged() to handleFoo(); foo2 =3D 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 =3D new Signal!()(this); sigValueChanged2 =3D 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 =3D new Foo; slotHandleFoo1 =3D new Slot!()(this, &handleFoo1); connect(foo.sigValueChanged1, slotHandleFoo1); foo2 =3D new Foo; slotHandleFoo2 =3D 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.
Sep 04 2006
parent reply Lutger <lutger.blijdestijn gmail.com> writes:
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 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; }
 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.
Sep 05 2006
next sibling parent reply Kristian <kjkilpi gmail.com> writes:
On Wed, 06 Sep 2006 03:50:02 +0300, Lutger <lutger.blijdestijn 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 a=
s =
 well.

 I think everybody will agree if I say that the optimal solution wou=
ld =
 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 =

me =
 disadvantages, for example boost::signals is quite different than QT's=
, =
 and the latter also has features for introspection. These libraries (a=
s =
 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 manag=
ed =
 part of my sigslot module, below I've added how your example looks lik=
e =
 in it. It also handles other callable types (like free functions). The=
re =
 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 =3D new Foo;
          foo.valueChanged1.connect(handleFoo(), this);

          foo2 =3D 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 =3D new Foo;
         connect foo.ValueChanged() to handleFoo();
          foo2 =3D 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 =3D new Signal!()(this);
         sigValueChanged2 =3D 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 =3D new Foo;
         slotHandleFoo1 =3D new Slot!()(this, &handleFoo1);
         connect(foo.sigValueChanged1, slotHandleFoo1);
          foo2 =3D new Foo;
         slotHandleFoo2 =3D 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.
Sep 07 2006
parent Lutger <lutger.blijdestijn gmail.com> writes:
Kristian wrote:
<snip>
 
 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...! ;)
Well thanks. I'll have to make some more tests before putting it online somewhere, then I'll see what it's worth.
 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.
It's more consistent with free functions / unmanaged slots: sig.connect(&fooFunc); // alternative syntax: sig ~= { writefln ("hello world"); };
 BTW, I'm wondering why there is 'void' in "Signal!(void, int, int)"? Is 
 it intentional?
Yes, it's the return value of the signature. Normally a signal returns whatever the last slot returns, if it does. With foreach traversal, you can do something similar to combiners in boost::signals. See http://www.boost.org/doc/html/signals.html. Here's an example: // sum for Signal!(int, <any type>) int sum(SIGT, ARGT) (SIGT signal, ARGT arg) { int result; foreach(slot; signal) result += slot(arg); return result; }
Sep 07 2006
prev sibling parent Serg Kovrov <kovrov no.spam> writes:
The approach (simplified), I currently use:

 class Foo
 {
 	enum EV {VAL_CHANGED,}
 	void emit(EV ev) {
 		auto fn = ev in subscribers;
 		try { if (fn != null) (*fn)(); }
 		catch (Exception wtf) { writefln("WTF? %s", wtf); }
 	}
 	void sub(EV ev, void delegate() fn) {
 		subscribers[ev] = fn;
 	}
 	int val(int i) {
 		value = i;
 		emit(EV.VAL_CHANGED);
 		return value;
 	}
 	int val() {
 		return value;
 	}
 	void delegate()[EV] subscribers;
 	int value;
 }
 
 class Bar
 {
 	this(Foo f) {
 		foo = f;
 		foo.sub(Foo.EV.VAL_CHANGED, &receive);
 	}
 	void receive() {
 		writefln("received %d", foo.val);
 
 	}
 	Foo foo;
 }
 
 void main()
 {
 	auto f = new Foo();
 	auto b = new Bar(f);
 	f.val = 1;
 	delete b;
 	f.val = 2;
 }
-- serg.
Sep 07 2006
prev sibling parent Agent Orange <no spam.com> writes:
Kristian wrote:
 It would be nice if D had a signal/slot mechanism similiar to Qt:
 http://doc.trolltech.com/4.1/signalsandslots.html
 
 It's an elegant way to handle messages sent between objects. It beats  
 event table declarations used by other GUI libraries IMHO.
 
 It would make D a lot more appealing language to write GUI 
 applications.  Think of wxWidgets written in D... ;)
 
 
 I think it would be quite simple to build a S/S support for a compiler 
 (at  first glance, at least). For example:
 
 The 'Object' class has a pointer to S/S data (it's null if the object  
 don't currently use signals/slots). S/S data holds a slot list for each  
 signal. It also holds a list of objects that have slot(s) connected to  
 this object's signal(s). This list is used to disconnect necessary 
 slots  at a destruction of the object.
 
 When the compiler reads a 'emit X' statement, it will do two things.  
 First, it generates an id for the signal which is used to retrieve a  
 correct slot list.
 
 Second, the compiler puts the signal's parameters to the stack as it 
 would  call a corresponding function. Instead, the 
 'Object._emit_signal(id)'  function (or something) is called, where 'id' 
 is the generated id. (Note  that there are no function bodies for 
 signals.) '_emit_signal()' retrieves  the correct slot list, and calls 
 all the slots (delegates) in it. Finally  the parameters are removed 
 from the stack.
 
 Of course, slots should not modify their parameters so that all the 
 slots  will receive the same parameter values. Hence slots should not 
 use the  'out type'. There is a market for a 'const type' here... *wink*
 
 
 Maybe there should be no slot keyword at all as there is in Qt. You 
 don't  need to declare a function to be a slot; all the (virtual) 
 functions can  be used with signals.
 
 Because the return values of all the signals are void, the void 
 typeword  could be removed from signal declarations.
 
 signal clicked();
 
 signals:
 clicked();
 clicked(int button);
 
 
 BTW, Qt generates ids for signals as follows:
 
 signals:
 void clicked(int button, bool isMoved);
 -> the id is a string "clicked(int,bool)"
This is a great idea. Trolltech really has created a new style of event based programming. Its especially useful for IU systems. The signal slot pattern is a generalized observer pattern with late binding. The signals are fired at potential observers and resolved via string identifier at runtime; this allows a great deal of tool-code integration. signal slot libraries tend to be a hastle because the observers have to disconnect from their signals on destruction - you cant fire a signal at a dead object, but Qts moc makes takes care of all of that incredibly well. in qt signals and slots are like first class language constructs, and it becomes less of a hastle and more of an amazingly usefull tool. D would be just absolutely incredible if it supported signals and slots, or even just the observer pattern - perhaps something in phobos that hooks into object destruction.... another option is to create a tool such as trolltechs moc but even then you would have to require users to link a library or something; or would be nice to see this attached to something like ares. signals and slots give you anonymous typesafe callbacks that allow a whole new level of object oriented abstraction that most people unfortunately cant fully understand until they use them. walter has said before he doesnt quite know what signals and slots are. I recently used Qt on a project and I immediately fell in love with them similarly to the way I fell for D. A mariage of the two could possibly create an incredible new style of programming.
Sep 03 2006