www.digitalmars.com         C & C++   DMDScript  

D - Signal/Slot mechanism?

reply Norbert Nemec <Norbert.Nemec gmx.de> writes:
Hi there,

just wondering: has anybody tried to implement a signal/slot mechanism in D?
I was thinking about it a little, but actually seem to run into trouble:

At first sight, delegates seem to solve the problem. Looking closer, though,
I believe they are insufficient for something in par with Qt's concept
signal/slot.

A naive implementation is fairly trivial: signals are objects that
encapsulate a list of delegates, any non-static class member functions can
be seen as slot and connected to signals.

One minor drawback here is the rather clumsy syntax needed to declare
signals. Lacking templates with variable number of parameters, you would
need different signal classes depending on the number of arguments.

The far more fundamental problem, though: *disconnecting*.

In Qt, every slot knows which signals are connected to it. This makes it
possible to disconnect all incoming signals when an object is deactivated
(I'm avoiding the term "deleted" here, since in D, deactivation would
happen independantly of the deletion that may be done by the GC lateron.)

Furthermore, a delegate contains a reference to the object that is binds to,
but there is no clean way to retrieve that reference from the delegate, so
it would not even be possible to include a "deactivated" flag in the class
containing the slot and check this before calling it. In the other way
around, you might think about checking for the "deactivated" flag inside
the slot implementation. But then, you still have no way to disconnect,
because from inside the slot you have no way to find out where the signal
was that you want to disconnect from.

Putting this all together, this means, that with a simple and comfortable
implementation using just plain delegates, you have to keep track of all
the connections you created in order to be able to disconnect by hand.
Otherwise, in a long-running GUI program, with many widgets created and
destroyed over time, you would end up with more and more dead connections
slowing down everything.

If, on the other hand, you implement slots not just as plain routines, but
as objects encapsulating a routine, you can, of course, implement this
auto-disconnect, but the syntax for slots will get really messy.

Any ideas?

Ciao,
Nobbi
Apr 23 2004
next sibling parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Norbert Nemec wrote:

Hi there,

just wondering: has anybody tried to implement a signal/slot mechanism in D?
I was thinking about it a little, but actually seem to run into trouble:

At first sight, delegates seem to solve the problem. Looking closer, though,
I believe they are insufficient for something in par with Qt's concept
signal/slot.

A naive implementation is fairly trivial: signals are objects that
encapsulate a list of delegates, any non-static class member functions can
be seen as slot and connected to signals.

One minor drawback here is the rather clumsy syntax needed to declare
signals. Lacking templates with variable number of parameters, you would
need different signal classes depending on the number of arguments.
  

Workaround: Put everything into a struct. You an even then add methods to the struct to give it more power (to do generally things with it's contained variables) then just passing in a group of variable arguments.
The far more fundamental problem, though: *disconnecting*.

In Qt, every slot knows which signals are connected to it. This makes it
possible to disconnect all incoming signals when an object is deactivated
(I'm avoiding the term "deleted" here, since in D, deactivation would
happen independantly of the deletion that may be done by the GC lateron.)

Furthermore, a delegate contains a reference to the object that is binds to,
but there is no clean way to retrieve that reference from the delegate, so
it would not even be possible to include a "deactivated" flag in the class
containing the slot and check this before calling it. In the other way
around, you might think about checking for the "deactivated" flag inside
the slot implementation. But then, you still have no way to disconnect,
because from inside the slot you have no way to find out where the signal
was that you want to disconnect from.

Putting this all together, this means, that with a simple and comfortable
implementation using just plain delegates, you have to keep track of all
the connections you created in order to be able to disconnect by hand.
Otherwise, in a long-running GUI program, with many widgets created and
destroyed over time, you would end up with more and more dead connections
slowing down everything.

If, on the other hand, you implement slots not just as plain routines, but
as objects encapsulating a routine, you can, of course, implement this
auto-disconnect, but the syntax for slots will get really messy.

Any ideas?

Ciao,
Nobbi
  

I don't know how helpful this will be but dig uses a dispatcher. And add the event to a class like struct rect { int x, y; //Other methods that apply } class A { DispatcherT!(rect).Dispatcher onResized; ... Called when resize occurs rect r; r.x = x; r.y = y; onResized.notify(r); ... } In this example you can declare an event like: class A { this() { onResized.add(&resize); } void resize(rect r) { ... } } //Note : opCalls simplify the syntax even more (but I didn't use them in this example). ////////////////////////////////////////////////////////////////////// /*! * \file * \brief * \author BurtonRadons (modified by Charles Sanders and Joel Anderson) * \version 1.0 * \date 4 / 9 / 2004 */ ////////////////////////////////////////////////////////////////////// template DispatcherT(Event) { /** A set of delegates that are notified when a a Control event is activated. */ struct Dispatcher { alias void delegate (Event event) Method; /**< The first-form method. */ alias void delegate () MethodB; /**< The second-form method. */ Method [] methods; /**< The list of methods called by notify. */ MethodB [] methodbs; /**< No-argument-form methods. */ Dispatcher *[] dispatchers; /**< Dispatcher pointers. */ /** Add a method to the list. Frees the previous list if append resulted in re-allocation. */ void add(Method method) { methods ~= method; } /** Add a no-argument-form method to the list. */ void add(MethodB method) { methodbs ~= method; } /** Add a dispatcher pointer to the list. */ void add(Dispatcher *dispatcher) { dispatchers ~= dispatcher; } /** Remove a method from the list. */ void remove (Method m) { for (int c; c < methods.length; c ++) if (m == methods [c]) { for ( ; c < methods.length - 1; c ++) methods [c] = methods [c + 1]; methods.length = methods.length - 1; } } /** Remove a method from the list. */ void remove (MethodB m) { for (int c; c < methodbs.length; c ++) if (m == methodbs [c]) { for ( ; c < methodbs.length - 1; c ++) methodbs [c] = methodbs [c + 1]; methodbs.length = methodbs.length - 1; } } /** Remove a dispatcher from the list. */ void remove (Dispatcher *d) { for (int c; c < dispatchers.length; c ++) if (d == dispatchers [c]) { for ( ; c < dispatchers.length - 1; c ++) dispatchers [c] = dispatchers [c + 1]; dispatchers.length = dispatchers.length - 1; } } /** Notify the methods that an event has occured. */ void notify (Event e) { for (Method *m = methods, n = m + methods.length; m < n; m ++) (*m) (e); for (MethodB *m = methodbs, n = m + methodbs.length; m < n; m ++) (*m) (); for (Dispatcher **m = dispatchers, n = m + dispatchers.length; m < n; m ++) (*m).notify (e); } /** Notify the methods with an empty event. */ void notify () { Event e; notify (e); } /** Notify this dispatcher if non-empty, or the other dispatcher if this one is. */ void notifyOrEmpty (Event e, Dispatcher *other) { if (methods.length) notify (e); else other.notify (e); } /** Notify this dispatcher if non-empty, or the other dispatcher if this one is. */ void notifyOrEmpty (Dispatcher *other) { Event e; notifyOrEmpty (e, other); } /** Return whether there are no entries in this dispatcher. */ bit isEmpty () { return methods.length == 0 && methodbs.length == 0 && dispatchers.length == 0; } /** Notify the methods with an empty event. */ void opCall() { notify(); } /** Notify the methods that an event has occured. */ void opCall(Event e) { notify(e); } /** Add a method to the list. Frees the previous list if append resulted in re-allocation. */ void opCall(Method method) { add(method); } /** Add a no-argument-form method to the list. */ void opCall(MethodB method) { add(method); } /** Add a dispatcher pointer to the list. */ void opCall(Dispatcher *dispatcher) { add(dispatcher); } } } -- -Anderson: http://badmama.com.au/~anderson/
Apr 23 2004
parent Norbert Nemec <Norbert.Nemec gmx.de> writes:
J Anderson wrote:

 Norbert Nemec wrote:
 
 Workaround: Put everything into a struct.   You an even then add methods
 to the struct to give it more power (to do generally things with it's
 contained variables) then just passing in a group of variable arguments.

OK, that is some kind of a solution: say that signals/slots always have exactly one argument of arbitrary type and then use a struct instead of multiple arguments. But anyway that's just a minor problem.
The far more fundamental problem, though: *disconnecting*.

I don't know how helpful this will be but dig uses a dispatcher.

That doesn't help at all. "dispatcher" basically is just another term for what Qt calls signal. The core problem stays there: You create your widgets and connect them wildly by adding delegates to dispatchers. Now you want to delete one widget. Along with it, you want to cut all the connections to it. To do so, you have to keep track of all the connections you created earlier. I don't see any way to do so automatically in some comfortable way. And doing it by hand causes similar problems as the manual memory management in C++. If you forget to cut some connection, you may have deleted the widget, but the connected routine will still be called and the GC will not be able to clean up the widget object. For simple, rather static programs, that is no big issue, but for big, long-running applications, you will collect lots of garbage and the program will get slower and slower because of all the calls to stale connections.
Apr 23 2004
prev sibling next sibling parent reply Bastiaan Veelo <Bastiaan.N.Veelo ntnu.no> writes:
I have not tried yet, but I have thought of porting libsigc++ to D as a 
way to learn D. It will have to wait until the autumn though, as I am 
tied up at the moment. Do you know that library? Does it use template 
functionality that is not in D?

http://libsigc.sourceforge.net/

Bastiaan.

Norbert Nemec wrote:
 Hi there,
 
 just wondering: has anybody tried to implement a signal/slot mechanism in D?
 I was thinking about it a little, but actually seem to run into trouble:
 
 At first sight, delegates seem to solve the problem. Looking closer, though,
 I believe they are insufficient for something in par with Qt's concept
 signal/slot.
 
 A naive implementation is fairly trivial: signals are objects that
 encapsulate a list of delegates, any non-static class member functions can
 be seen as slot and connected to signals.
 
 One minor drawback here is the rather clumsy syntax needed to declare
 signals. Lacking templates with variable number of parameters, you would
 need different signal classes depending on the number of arguments.
 
 The far more fundamental problem, though: *disconnecting*.
 
 In Qt, every slot knows which signals are connected to it. This makes it
 possible to disconnect all incoming signals when an object is deactivated
 (I'm avoiding the term "deleted" here, since in D, deactivation would
 happen independantly of the deletion that may be done by the GC lateron.)
 
 Furthermore, a delegate contains a reference to the object that is binds to,
 but there is no clean way to retrieve that reference from the delegate, so
 it would not even be possible to include a "deactivated" flag in the class
 containing the slot and check this before calling it. In the other way
 around, you might think about checking for the "deactivated" flag inside
 the slot implementation. But then, you still have no way to disconnect,
 because from inside the slot you have no way to find out where the signal
 was that you want to disconnect from.
 
 Putting this all together, this means, that with a simple and comfortable
 implementation using just plain delegates, you have to keep track of all
 the connections you created in order to be able to disconnect by hand.
 Otherwise, in a long-running GUI program, with many widgets created and
 destroyed over time, you would end up with more and more dead connections
 slowing down everything.
 
 If, on the other hand, you implement slots not just as plain routines, but
 as objects encapsulating a routine, you can, of course, implement this
 auto-disconnect, but the syntax for slots will get really messy.
 
 Any ideas?
 
 Ciao,
 Nobbi

Apr 22 2004
next sibling parent reply Stephan Wienczny <wienczny web.de> writes:
Bastiaan Veelo wrote:

 I have not tried yet, but I have thought of porting libsigc++ to D as a 
 way to learn D. It will have to wait until the autumn though, as I am 
 tied up at the moment. Do you know that library? Does it use template 
 functionality that is not in D?
 
 http://libsigc.sourceforge.net/
 
 Bastiaan.
 

Is there something I can do in C++, I can't do in D?
Apr 23 2004
parent Norbert Nemec <Norbert.Nemec gmx.de> writes:
Stephan Wienczny wrote:

 Bastiaan Veelo wrote:
 
 I have not tried yet, but I have thought of porting libsigc++ to D as a
 way to learn D. It will have to wait until the autumn though, as I am
 tied up at the moment. Do you know that library? Does it use template
 functionality that is not in D?
 
 http://libsigc.sourceforge.net/
 
 Bastiaan.
 

Is there something I can do in C++, I can't do in D?

Well - the template mechanism is completely different, so for many things you will at least have to do them differently. libsigc++ really squeezes everything out of the C++-template mechanism, so it is hard to tell where you might find yet-unknown limitations of D. B.t.w: I have no doubt you can do everything in D - the question only is whether it is still comfortable and efficient.
Apr 23 2004
prev sibling parent Norbert Nemec <Norbert.Nemec gmx.de> writes:
Hey, I guess I should have looked at libsigc++ earlier. It really give me
the idea of a solution:

If you introduce a class "Slot", then of course, it could take two arguments
as constructor arguments: a delegate and, additionally, a reference to the
object that the delegate points to.

OK, it looks a bit strange at first:
        sigsomething.connect(Slot(someobject,&(someobject.routine))
(in C++ this could be solved by a macro, which we don't have in D)
and actually, you pass the same pointer twice, but what the heck - it allows
you to implement that automatic disconnection, since the Slot creator could
simply register itself in someobject - which of course has to inherit from
some basic class.

Porting libsigc++ to D would definitely be something very interesting. I
guess you really have to dig into the language. If any necessary
functionality in D is missing it would be highly interesting to
investigate.

Anyhow: I would suggest not to try to stick to libsigc++ too closely. Pick
up the concepts and ideas and then see how you can do something at least as
good in D. I guess, you can even surpass what libsigc++ is doing, since D
just has a number of very powerful features.
Apr 23 2004
prev sibling next sibling parent reply "Achilleas Margaritis" <axilmar in.gr> writes:
"Norbert Nemec" <Norbert.Nemec gmx.de> wrote in message
news:c6afc1$2eh6$1 digitaldaemon.com...
 Hi there,

 just wondering: has anybody tried to implement a signal/slot mechanism in

 I was thinking about it a little, but actually seem to run into trouble:

 At first sight, delegates seem to solve the problem. Looking closer,

 I believe they are insufficient for something in par with Qt's concept
 signal/slot.

 A naive implementation is fairly trivial: signals are objects that
 encapsulate a list of delegates, any non-static class member functions can
 be seen as slot and connected to signals.

 One minor drawback here is the rather clumsy syntax needed to declare
 signals. Lacking templates with variable number of parameters, you would
 need different signal classes depending on the number of arguments.

 The far more fundamental problem, though: *disconnecting*.

 In Qt, every slot knows which signals are connected to it. This makes it
 possible to disconnect all incoming signals when an object is deactivated
 (I'm avoiding the term "deleted" here, since in D, deactivation would
 happen independantly of the deletion that may be done by the GC lateron.)

 Furthermore, a delegate contains a reference to the object that is binds

 but there is no clean way to retrieve that reference from the delegate, so
 it would not even be possible to include a "deactivated" flag in the class
 containing the slot and check this before calling it. In the other way
 around, you might think about checking for the "deactivated" flag inside
 the slot implementation. But then, you still have no way to disconnect,
 because from inside the slot you have no way to find out where the signal
 was that you want to disconnect from.

 Putting this all together, this means, that with a simple and comfortable
 implementation using just plain delegates, you have to keep track of all
 the connections you created in order to be able to disconnect by hand.
 Otherwise, in a long-running GUI program, with many widgets created and
 destroyed over time, you would end up with more and more dead connections
 slowing down everything.

 If, on the other hand, you implement slots not just as plain routines, but
 as objects encapsulating a routine, you can, of course, implement this
 auto-disconnect, but the syntax for slots will get really messy.

 Any ideas?

 Ciao,
 Nobbi

It is very easy to implement signals and slots in D. The mechanism is the same as in C++, with the only difference being that since a D object is not deleted unless no other object points to it, you have to manually disconnect an object. In fact, it is way more easier in D, because of delegates. Here is the code for a signal with 1 parameter: public class Signal(T) { public alias void delegate(T) Delegate; void exec(T param) { foreach(Delegate dg; m_slots) { dg(param); } } void add(Delegate dg) { m_slots.add(dg); } void remove(Delegate dg) { m_slots.remove(dg); } private List!(Delegate) m_slots; } Suppose that there is a List template class, the signal becomes a list of delegates, with the following interface: add(Delegate) remove(Delegate) exec(T param) It can be used like this: Signal!(int) callback; void proc1(int) { } class Foo { public void action(int i) { } } //add a function callback.add(&proc1); //add a method Foo foo1 = new Foo; callback.add(&foo1.action); //call the signal; will call proc1 and foo1.action callback(10);
Apr 23 2004
next sibling parent J Anderson <REMOVEanderson badmama.com.au> writes:
Achilleas Margaritis wrote:

It is very easy to implement signals and slots in D. The mechanism is the
same as in C++, with the only difference being that since a D object is not
deleted unless no other object points to it, you have to manually disconnect
an object.

In fact, it is way more easier in D, because of delegates.

Here is the code for a signal with 1 parameter:

public class Signal(T) {
 public alias void delegate(T) Delegate;

 void exec(T param) {
    foreach(Delegate dg; m_slots) {
       dg(param);
    }
 }

 void add(Delegate dg) {
    m_slots.add(dg);
 }

 void remove(Delegate dg) {
    m_slots.remove(dg);
 }

   private List!(Delegate) m_slots;
}

Suppose that there is a List template class, the signal becomes a list of
delegates, with the following interface:

add(Delegate)

remove(Delegate)

exec(T param)

It can be used like this:

Signal!(int) callback;

void proc1(int)
{
}

class Foo {
    public void action(int i) {
    }
}

//add a function
callback.add(&proc1);

//add a method
Foo foo1 = new Foo;
callback.add(&foo1.action);

//call the signal; will call proc1 and foo1.action
callback(10);
  

-- -Anderson: http://badmama.com.au/~anderson/
Apr 23 2004
prev sibling next sibling parent reply Bastiaan Veelo <Bastiaan.N.Veelo ntnu.no> writes:
Achilleas Margaritis wrote:
 
 It is very easy to implement signals and slots in D. The mechanism is the
 same as in C++, with the only difference being that since a D object is not
 deleted unless no other object points to it, you have to manually disconnect
 an object.
 
 In fact, it is way more easier in D, because of delegates.
 
 Here is the code for a signal with 1 parameter:
 
 public class Signal(T) {
  public alias void delegate(T) Delegate;
 
  void exec(T param) {
     foreach(Delegate dg; m_slots) {
        dg(param);
     }
  }
 
  void add(Delegate dg) {
     m_slots.add(dg);
  }
 
  void remove(Delegate dg) {
     m_slots.remove(dg);
  }
 
    private List!(Delegate) m_slots;
 }
 
 Suppose that there is a List template class, the signal becomes a list of
 delegates, with the following interface:
 
 add(Delegate)
 
 remove(Delegate)
 
 exec(T param)
 
 It can be used like this:
 
 Signal!(int) callback;
 
 void proc1(int)
 {
 }
 
 class Foo {
     public void action(int i) {
     }
 }
 
 //add a function
 callback.add(&proc1);
 
 //add a method
 Foo foo1 = new Foo;
 callback.add(&foo1.action);
 
 //call the signal; will call proc1 and foo1.action
 callback(10);
 
 

I may be missing something here, but what if we want to delete foo1? Do we need to check all instances of Signal to look for Delegates that should be removed? I guess this needs a Slot object in Norbert's style. Once I get around to this, it'll be an interesting exercise. Bastiaan.
Apr 23 2004
parent "Ben Hinkle" <bhinkle4 juno.com> writes:
 I may be missing something here, but what if we want to delete foo1? Do
 we need to check all instances of Signal to look for Delegates that
 should be removed? I guess this needs a Slot object in Norbert's style.
 Once I get around to this, it'll be an interesting exercise.

There are probably several approaches, but personally I'd say whatever registered the delegate should have an API to unregister the same delegate. As previously pointed it is easy to unregister a delegate. The hard/impossible part is taking an arbitrary object and testing if a delegate is a delegate for that object. So design the API to unregister delegates instead of unregsitering objects. That will also cover the cases when the delegates are for stack frames as well as objects. -Ben
Apr 23 2004
prev sibling parent reply Norbert Nemec <Norbert.Nemec gmx.de> writes:
As mentioned by others, this really misses the point:

It truely is simple and elegant to construct signals and slots in D. It was
just the *disconnection* that gave me trouble. Having to keep track of all
connections by hand is rather tedious.

Anyway: The idea by Ben Hinkle really would allow to solve the problem. (See
my other message)


Achilleas Margaritis wrote:

 
 "Norbert Nemec" <Norbert.Nemec gmx.de> wrote in message
 news:c6afc1$2eh6$1 digitaldaemon.com...
 Hi there,

 just wondering: has anybody tried to implement a signal/slot mechanism in

 I was thinking about it a little, but actually seem to run into trouble:

 At first sight, delegates seem to solve the problem. Looking closer,

 I believe they are insufficient for something in par with Qt's concept
 signal/slot.

 A naive implementation is fairly trivial: signals are objects that
 encapsulate a list of delegates, any non-static class member functions
 can be seen as slot and connected to signals.

 One minor drawback here is the rather clumsy syntax needed to declare
 signals. Lacking templates with variable number of parameters, you would
 need different signal classes depending on the number of arguments.

 The far more fundamental problem, though: *disconnecting*.

 In Qt, every slot knows which signals are connected to it. This makes it
 possible to disconnect all incoming signals when an object is deactivated
 (I'm avoiding the term "deleted" here, since in D, deactivation would
 happen independantly of the deletion that may be done by the GC lateron.)

 Furthermore, a delegate contains a reference to the object that is binds

 but there is no clean way to retrieve that reference from the delegate,
 so it would not even be possible to include a "deactivated" flag in the
 class containing the slot and check this before calling it. In the other
 way around, you might think about checking for the "deactivated" flag
 inside the slot implementation. But then, you still have no way to
 disconnect, because from inside the slot you have no way to find out
 where the signal was that you want to disconnect from.

 Putting this all together, this means, that with a simple and comfortable
 implementation using just plain delegates, you have to keep track of all
 the connections you created in order to be able to disconnect by hand.
 Otherwise, in a long-running GUI program, with many widgets created and
 destroyed over time, you would end up with more and more dead connections
 slowing down everything.

 If, on the other hand, you implement slots not just as plain routines,
 but as objects encapsulating a routine, you can, of course, implement
 this auto-disconnect, but the syntax for slots will get really messy.

 Any ideas?

 Ciao,
 Nobbi

It is very easy to implement signals and slots in D. The mechanism is the same as in C++, with the only difference being that since a D object is not deleted unless no other object points to it, you have to manually disconnect an object. In fact, it is way more easier in D, because of delegates. Here is the code for a signal with 1 parameter: public class Signal(T) { public alias void delegate(T) Delegate; void exec(T param) { foreach(Delegate dg; m_slots) { dg(param); } } void add(Delegate dg) { m_slots.add(dg); } void remove(Delegate dg) { m_slots.remove(dg); } private List!(Delegate) m_slots; } Suppose that there is a List template class, the signal becomes a list of delegates, with the following interface: add(Delegate) remove(Delegate) exec(T param) It can be used like this: Signal!(int) callback; void proc1(int) { } class Foo { public void action(int i) { } } //add a function callback.add(&proc1); //add a method Foo foo1 = new Foo; callback.add(&foo1.action); //call the signal; will call proc1 and foo1.action callback(10);

Apr 23 2004
next sibling parent reply Colin JN Breame <Colin_member pathlink.com> writes:
How about using something like Weak References.  These are references that allow
an object to be garbage collected if it only has weak references left and no
others.  The weak reference is set to null when the object is collected.

This has the advantage that delegate would not have to be removed from the
signal.

This would mean that the language and garbage collection would have to be
modified.

In article <c6bbsk$srp$2 digitaldaemon.com>, Norbert Nemec says...
As mentioned by others, this really misses the point:

It truely is simple and elegant to construct signals and slots in D. It was
just the *disconnection* that gave me trouble. Having to keep track of all
connections by hand is rather tedious.

Anyway: The idea by Ben Hinkle really would allow to solve the problem. (See
my other message)


Achilleas Margaritis wrote:

 
 "Norbert Nemec" <Norbert.Nemec gmx.de> wrote in message
 news:c6afc1$2eh6$1 digitaldaemon.com...
 Hi there,

 just wondering: has anybody tried to implement a signal/slot mechanism in

 I was thinking about it a little, but actually seem to run into trouble:

 At first sight, delegates seem to solve the problem. Looking closer,

 I believe they are insufficient for something in par with Qt's concept
 signal/slot.

 A naive implementation is fairly trivial: signals are objects that
 encapsulate a list of delegates, any non-static class member functions
 can be seen as slot and connected to signals.

 One minor drawback here is the rather clumsy syntax needed to declare
 signals. Lacking templates with variable number of parameters, you would
 need different signal classes depending on the number of arguments.

 The far more fundamental problem, though: *disconnecting*.

 In Qt, every slot knows which signals are connected to it. This makes it
 possible to disconnect all incoming signals when an object is deactivated
 (I'm avoiding the term "deleted" here, since in D, deactivation would
 happen independantly of the deletion that may be done by the GC lateron.)

 Furthermore, a delegate contains a reference to the object that is binds

 but there is no clean way to retrieve that reference from the delegate,
 so it would not even be possible to include a "deactivated" flag in the
 class containing the slot and check this before calling it. In the other
 way around, you might think about checking for the "deactivated" flag
 inside the slot implementation. But then, you still have no way to
 disconnect, because from inside the slot you have no way to find out
 where the signal was that you want to disconnect from.

 Putting this all together, this means, that with a simple and comfortable
 implementation using just plain delegates, you have to keep track of all
 the connections you created in order to be able to disconnect by hand.
 Otherwise, in a long-running GUI program, with many widgets created and
 destroyed over time, you would end up with more and more dead connections
 slowing down everything.

 If, on the other hand, you implement slots not just as plain routines,
 but as objects encapsulating a routine, you can, of course, implement
 this auto-disconnect, but the syntax for slots will get really messy.

 Any ideas?

 Ciao,
 Nobbi

It is very easy to implement signals and slots in D. The mechanism is the same as in C++, with the only difference being that since a D object is not deleted unless no other object points to it, you have to manually disconnect an object. In fact, it is way more easier in D, because of delegates. Here is the code for a signal with 1 parameter: public class Signal(T) { public alias void delegate(T) Delegate; void exec(T param) { foreach(Delegate dg; m_slots) { dg(param); } } void add(Delegate dg) { m_slots.add(dg); } void remove(Delegate dg) { m_slots.remove(dg); } private List!(Delegate) m_slots; } Suppose that there is a List template class, the signal becomes a list of delegates, with the following interface: add(Delegate) remove(Delegate) exec(T param) It can be used like this: Signal!(int) callback; void proc1(int) { } class Foo { public void action(int i) { } } //add a function callback.add(&proc1); //add a method Foo foo1 = new Foo; callback.add(&foo1.action); //call the signal; will call proc1 and foo1.action callback(10);


Apr 23 2004
next sibling parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Colin JN Breame wrote:

How about using something like Weak References.  These are references that allow
an object to be garbage collected if it only has weak references left and no
others.  The weak reference is set to null when the object is collected.

This has the advantage that delegate would not have to be removed from the
signal.

This would mean that the language and garbage collection would have to be
modified.

It would be nice if the GC could automaticly null delegates that have objects that no-longer exist. Well maybe it wouldn't. Then you'd have all these access violations (which of course you could test for as a performace cost). -- -Anderson: http://badmama.com.au/~anderson/
Apr 23 2004
parent Bastiaan Veelo <Bastiaan.N.Veelo ntnu.no> writes:
J Anderson wrote:
 
 Well maybe it wouldn't.  Then you'd have all these access violations 
 (which of course you could test for as a performace cost).
 

Can access violations be cought as an exception? Bastiaan.
Apr 23 2004
prev sibling next sibling parent Andy Friesen <andy ikagames.com> writes:
Colin JN Breame wrote:
 How about using something like Weak References.  These are references that
allow
 an object to be garbage collected if it only has weak references left and no
 others.  The weak reference is set to null when the object is collected.
 
 This has the advantage that delegate would not have to be removed from the
 signal.
 
 This would mean that the language and garbage collection would have to be
 modified.

I'd love to see weak references implemented in D. (be it by altering the language, or implemented by some template wizardry) -- andy
Apr 23 2004
prev sibling parent Bastiaan Veelo <Bastiaan.N.Veelo ntnu.no> writes:
Colin JN Breame wrote:
 How about using something like Weak References.  These are references that
allow
 an object to be garbage collected if it only has weak references left and no
 others.  The weak reference is set to null when the object is collected.
 

I see two problems: 1) Upon every emit of the signal, all weak delegates would have to be tested for non-nullness. 2) What happens when the receiving object has gone out of scope, but the GC has not yet been run? The object would still receive the signal.
 This has the advantage that delegate would not have to be removed from the
 signal.
 

The signal could clean itself up, removing delegates that are null. It is not very elegant, though.
 This would mean that the language and garbage collection would have to be
 modified.
 

I doubt that the advantages of weak delegates are worth such a change. Bastiaan.
Apr 23 2004
prev sibling parent Achilleas Margaritis <axilmar b-online.gr> writes:
 As mentioned by others, this really misses the point:
 
 It truely is simple and elegant to construct signals and slots in D. It was
 just the *disconnection* that gave me trouble. Having to keep track of all
 connections by hand is rather tedious.

You just have to make a superclass for all objects that have slots: class SlotContainer { public ~this() { clearSlots(); } public addSlot(Slot slot); public removeSlot(Slot slot); public void clearSlots() { delete all slots; } } The signal would add a slot to the object: class Signal { public void add(SlotContainer container, void delegate(T) dg) { m_slots.add(dg); Slot slot = new Slot(dg); slot.m_signals.add(this); container.addSlot(slot); } } When the slot is deleted, it removes itself from all the signals it belongs: class Slot { public ~this() { foreach(Signal sig ; m_signals) { sig.remove(this); } } } Then, each time a SlotContainer-derived object is deleted, it will also delete all its slots, and the slots would unregister themselves from the objects they are registered into.
Apr 23 2004
prev sibling next sibling parent reply Ben Hinkle <bhinkle4 juno.com> writes:
[snip]
 Furthermore, a delegate contains a reference to the object that is binds
 to, but there is no clean way to retrieve that reference from the
 delegate

It would be nice to have a few properties on delegates to split it apart. One wrinkle is that delegates can also use stack frames as the "object" so any return type of a property for the object would have to return void* or something like that. A "temporary" workaround is to delve into implementation specific hackery: struct DelegateStruct { void* frame_or_obj; void* fcn; } class Foo { void foo() { } } int main() { Foo x = new Foo(); void delegate () y = &x.foo; DelegateStruct *y2 = (DelegateStruct*)&y; printf("x=%p, y2.frame_or_obj=%p\n",x,y2.frame_or_obj); return 0; }
Apr 23 2004
parent Norbert Nemec <Norbert.Nemec gmx.de> writes:
That's a neat idea! Very "D-ish"! Just define a .object property on the
delegate. It would, of course, be of type "Object".

The Object class could then have two methods
        void connect_notify(Connection) {};
        void disconnect_notify(Connection) {};
that could be overridden by subclasses that want to keep track of
connections that want to have automatic disconnection!

Guess, there we have the full solution for my problem. And I really doubt
there are any objections against that ".object" property proposal (ouch,
what an evil pun!)
Apr 23 2004
prev sibling parent reply Andy Friesen <andy ikagames.com> writes:
Norbert Nemec wrote:
 Hi there,
 
 just wondering: has anybody tried to implement a signal/slot mechanism in D?
 I was thinking about it a little, but actually seem to run into trouble:
 
 At first sight, delegates seem to solve the problem. Looking closer, though,
 I believe they are insufficient for something in par with Qt's concept
 signal/slot.
 
 A naive implementation is fairly trivial: signals are objects that
 encapsulate a list of delegates, any non-static class member functions can
 be seen as slot and connected to signals.
 
 One minor drawback here is the rather clumsy syntax needed to declare
 signals. Lacking templates with variable number of parameters, you would
 need different signal classes depending on the number of arguments.

Templates can be overloaded, so you can kinda-sorta work around this. <http://andy.tadan.us/d/Listener.d> (coding it is a bit tedious, but the usage is nice enough) This doesn't deal with the disconnection thing either, though. -- andy
Apr 23 2004
parent reply Norbert Nemec <Norbert.Nemec gmx.de> writes:
Andy Friesen wrote:
 Templates can be overloaded, so you can kinda-sorta work around this.
 <http://andy.tadan.us/d/Listener.d> (coding it is a bit tedious, but the
 usage is nice enough)

Hey, thanks - nice to know that this works. I thought about it, but didn't try it yet.
 This doesn't deal with the disconnection thing either, though.

For that, I have the solution as well, now.
Apr 23 2004
parent reply Achilleas Margaritis <axilmar b-online.gr> writes:
Norbert Nemec wrote:

 Andy Friesen wrote:
 
Templates can be overloaded, so you can kinda-sorta work around this.
<http://andy.tadan.us/d/Listener.d> (coding it is a bit tedious, but the
usage is nice enough)

Hey, thanks - nice to know that this works. I thought about it, but didn't try it yet.
This doesn't deal with the disconnection thing either, though.

For that, I have the solution as well, now.

See my other post for the disconnection problem. Basically, it involves registering the slot to the signal and the signal to the slot. When the slot's owner is deleted, the slot is deleted, thus removing itself from the signal. But I wanted to say something, here, to Walter: Walter, signals and slots need support at language level. Templates are ugly, and one has to provide lots of different template classes, with different names, for each number of parameters. Signals and slots should follow delegate syntax, using the keyword 'signal', like this: ReturnType signal ( Arguments ) Identifier ; Signals should contain weak references, as mentioned above, and when the objects that are registered to signals are deleted, slots are automatically deleted. It is an important functionality Walter, please don't ignore it.
Apr 23 2004
next sibling parent reply "Walter" <walter digitalmars.com> writes:
"Achilleas Margaritis" <axilmar b-online.gr> wrote in message
news:c6c32m$2447$1 digitaldaemon.com...
 But I wanted to say something, here, to Walter:

 Walter, signals and slots need support at language level. Templates are

 and one has to provide lots of different template classes, with different

   for each number of parameters.

 Signals and slots should follow delegate syntax, using the keyword

 like this:

 ReturnType signal ( Arguments ) Identifier ;

 Signals should contain weak references, as mentioned above, and when the

 that are registered to signals are deleted, slots are automatically

 It is an important functionality Walter, please don't ignore it.

Frankly, I don't understand signals and slots at all.
Apr 23 2004
next sibling parent reply "Achilleas Margaritis" <axilmar in.gr> writes:
"Walter" <walter digitalmars.com> wrote in message
news:c6ca6t$2g9a$1 digitaldaemon.com...
 "Achilleas Margaritis" <axilmar b-online.gr> wrote in message
 news:c6c32m$2447$1 digitaldaemon.com...
 But I wanted to say something, here, to Walter:

 Walter, signals and slots need support at language level. Templates are

 and one has to provide lots of different template classes, with


 names,
   for each number of parameters.

 Signals and slots should follow delegate syntax, using the keyword

 like this:

 ReturnType signal ( Arguments ) Identifier ;

 Signals should contain weak references, as mentioned above, and when the

 that are registered to signals are deleted, slots are automatically

 It is an important functionality Walter, please don't ignore it.

Frankly, I don't understand signals and slots at all.

If you don't mind me explaining it, here is a short explanation: A signal is a list of callbacks. When a signal is called, all the callbacks are called in the order that they are registered. A callback is either a function or a method...in other words, a delegate. Callbacks can be arbitrarily added and removed to one or more signals. In some libraries (Qt for example), a function or method has to be explicitly marked as a callback, otherwise it can not be added to a signal. This 'marking' turns a callback to a 'slot', i.e. an explicitly defined signal target. The addition of a slot/callback/delegate/whatever to a signal is called a 'connection'. The removal of a slot/callback/delegate/whatever from a signal is called a 'disconnection'. Here is an example: A dialog has two methods 'accept' and 'reject', called when the user presses ok/enter or cancel/escape respectively. A PushButton class contains a 'click' signal: when the user clicks the button, this signal is called. The best and easiest way to connect a pushbutton with a dialog is to have 2 pushbuttons, one for the ok and one for the cancel case. The dialog's accept method is connected to the ok button's click signal; the dialog's reject method is connected to the cancel button's click signal. Now, when the user presses the ok button, the dialog's accept method is called, and the dialog is accepted. When the user presses the cancel button, the dialog's reject method is called, and the dialog is rejected. The signals and slots mechanism is the best way to manage the model-view-controller pattern: a) the model contains signals about when it is modified. b) the view contains slots to update the display when the model changes. c) the controller fires signals of the model, by modifying the model. The most important property of the signals and slots mechanism is that objects are not aware of the presence of other objects. In other words, a pushbutton does not need to know anything about the presence of a dialog. The pushbutton just says 'click' to the outside world, and whoever is listening takes action. Signals and slots can be done with C++ templates, but you need one template for each number of parameters that exist. That is why libsig++ or boost have classes like signal0, signal1, signal2, signal3 etc for 0, 1, 2, 3 or more parameters. Objects that have methods which can be slots must inherit from a specific class, which keeps track of which slots belong to which objects and automatically removes the slots from their signals when deleted. Java has anonymous functions: each object (let's say a pushbutton) executes a specific function which usually belongs to some other object (let's say a dialog). Anonymous functions are coded inline at the place of event instantiation. This is an ugly solution, because a class becomes a huge file with functions spreaded here and there. Sun has been critisized for it a lot. C# has the 'event', which is a synonymous to signal. You declare an event like this: event click(int data); And then you add and delete methods to it, like this: click += dialog.accept; This is an elegant solution, but it requires for the destination object to keep track where it has added its methods, so it can remove them when no longer used. What we are asking from you, Walter, is to provide a signal and slot mechanism, that not only reflects the way C# works, but it also allows for automatic removal of slots when an object is deleted, ala C++. If you don't mind, let me give you my view about how the syntax of signals and slots should be: signal Identifier ( Arguments ) ; The signal word should be a keyword. The following operations should be allowed on signals: signal += (delegate); signal -= (delegate); signal(Arguments); The operator += adds a delegate to a signal (only if it has not been added yet, of course); the operator -= removes a delegate from a signal; the operator () calls the signal with the given arguments. Here is the implementation: A signal is a linked list of delegates. Each time operator += is called, a delegate is added to the linked list of delegates the signal contains. Each time operator -= is called, a delegate is removed from the linked list of delegates. When the signal is called, the linked list of delegates is traversed and each delegate is called. Here is the important detail, concering delegates that are methods: The Object class should contain a linked list of pointers to delegates. each time a delegate is added to the signal, a pointer to the delegate is added to the Object-derived object. This pointer is not checked by the garbage collector (it is a weak reference). When the object is deleted, the delegates that the object contains are automatically removed from the signals they belong. In this way, an Object does not have to track in which signals it is registered to. Please, Walter, take this into consideration. I would not have spent so much time, carefully handcrafting this post, if it was not important. D is the best language there is, and the mechanisms are there for the signals and slots mechanism to be implemented(from what I have seen of the dmd sources). It would take you a couple of hours to implement it, but it will save thousands of hours of frustration from D programmers. You say in the documentation that some things are better be part of the language, instead of being done by libraries. You added 'complex' and 'map' (associative array) to the language for this reason. But signals and slots are much more important than either complex and map, I think that they should be part of the language, for elegance...it is one of those mechanisms, that has to be part of D. Don't hesitate to ask me anything about signals and slots...I will be more than happy to answer.
Apr 24 2004
next sibling parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Achilleas Margaritis wrote:

You say in the documentation that some things are better be part of the
language, instead of being done by libraries.
You added 'complex' and 'map' (associative array) to the language for this
reason.
But signals and slots are much more important than either complex and map, I
think that they should be part of the language, for elegance...it is one of
those mechanisms, that has to be part of D.
  

some language support was provided for removing delegates that reference objects that no-longer exist. -- -Anderson: http://badmama.com.au/~anderson/
Apr 24 2004
parent reply "Achilleas Margaritis" <axilmar in.gr> writes:

some language support was provided for removing delegates that reference objects that no-longer exist.

There is also the problem of names: it is ugly to have signal0, signal1, signal2 etc.
Apr 25 2004
parent reply Norbert Nemec <Norbert.Nemec gmx.de> writes:
Achilleas Margaritis wrote:


some language support was provided for removing delegates that reference objects that no-longer exist.

There is also the problem of names: it is ugly to have signal0, signal1, signal2 etc.

That's not a problem in D: you can have templates with different number of parameters but the same name, and even templates with zero arguments. (A non-templated class would clash with the name of a template, but a zero-parameter class can have the same name as a one-parameter class)
Apr 25 2004
parent reply "Achilleas Margaritis" <axilmar in.gr> writes:
 That's not a problem in D: you can have templates with different number of
 parameters but the same name, and even templates with zero arguments. (A
 non-templated class would clash with the name of a template, but a
 zero-parameter class can have the same name as a one-parameter class)

D is even cooler than I thought. :-) Anyway, a signals and slots mechanism not supported by the language also has other problems: 1) one has to cater for the different number of parameters. Libsig++ has 20 template signals, all doing the same thing. 2) objects should inherit from a specific class that knows about how to get rid of slots when deleted. If they don't inherit from the class, they can't be connected to slots. 3) specifically for garbage collected languages, object pointers in slots must not be garbage collected; otherwise, the GC will never delete the objects, unless the slots are explicitely removed from their signals, which is not desired.
Apr 25 2004
parent Norbert Nemec <Norbert.Nemec gmx.de> writes:
Achilleas Margaritis wrote:

 That's not a problem in D: you can have templates with different number
 of parameters but the same name, and even templates with zero arguments.
 (A non-templated class would clash with the name of a template, but a
 zero-parameter class can have the same name as a one-parameter class)

D is even cooler than I thought. :-) Anyway, a signals and slots mechanism not supported by the language also has other problems: 1) one has to cater for the different number of parameters. Libsig++ has 20 template signals, all doing the same thing.

Yes, true, we need to copy/paste for every number of arguments. I doubt, 20 would be necessary, though...
 2) objects should inherit from a specific class that knows about how to
 get rid of slots when deleted. If they don't inherit from the class, they
 can't be connected to slots.

True, lacking multiple inheritance, you have to put that code right into the base of any class hierarchy you build. Alternatively, you can use an interface that slot-aware objects have to implement to get auto-disconnection. Not too much of a problem: you would put that code way up in the hierarchy of any library anyway.
 3) specifically for garbage collected languages, object pointers in slots
 must not be garbage collected; otherwise, the GC will never delete the
 objects, unless the slots are explicitely removed from their signals,
 which is not desired.

True, from the GC point of view, signal-slot-connections should be handled as one-way pointers, even if the implementation needs pointers in both directions to enable auto-disconnection. Maybe there should be a way to specify pointers that should not be traversed, when the garbage collector marks life objects?
Apr 25 2004
prev sibling parent reply Colin JN Breame <c.j.n.breame dur.ac.uk> writes:
Seconded - this is exactly how it should work.  Any idea how we can get Walter
to take a look at this proposal?

In article <c6ehdg$1vkk$1 digitaldaemon.com>, Achilleas Margaritis says...
"Walter" <walter digitalmars.com> wrote in message
news:c6ca6t$2g9a$1 digitaldaemon.com...
 "Achilleas Margaritis" <axilmar b-online.gr> wrote in message
 news:c6c32m$2447$1 digitaldaemon.com...
 But I wanted to say something, here, to Walter:

 Walter, signals and slots need support at language level. Templates are

 and one has to provide lots of different template classes, with


 names,
   for each number of parameters.

 Signals and slots should follow delegate syntax, using the keyword

 like this:

 ReturnType signal ( Arguments ) Identifier ;

 Signals should contain weak references, as mentioned above, and when the

 that are registered to signals are deleted, slots are automatically

 It is an important functionality Walter, please don't ignore it.

Frankly, I don't understand signals and slots at all.

If you don't mind me explaining it, here is a short explanation: A signal is a list of callbacks. When a signal is called, all the callbacks are called in the order that they are registered. A callback is either a function or a method...in other words, a delegate. Callbacks can be arbitrarily added and removed to one or more signals. In some libraries (Qt for example), a function or method has to be explicitly marked as a callback, otherwise it can not be added to a signal. This 'marking' turns a callback to a 'slot', i.e. an explicitly defined signal target. The addition of a slot/callback/delegate/whatever to a signal is called a 'connection'. The removal of a slot/callback/delegate/whatever from a signal is called a 'disconnection'. Here is an example: A dialog has two methods 'accept' and 'reject', called when the user presses ok/enter or cancel/escape respectively. A PushButton class contains a 'click' signal: when the user clicks the button, this signal is called. The best and easiest way to connect a pushbutton with a dialog is to have 2 pushbuttons, one for the ok and one for the cancel case. The dialog's accept method is connected to the ok button's click signal; the dialog's reject method is connected to the cancel button's click signal. Now, when the user presses the ok button, the dialog's accept method is called, and the dialog is accepted. When the user presses the cancel button, the dialog's reject method is called, and the dialog is rejected. The signals and slots mechanism is the best way to manage the model-view-controller pattern: a) the model contains signals about when it is modified. b) the view contains slots to update the display when the model changes. c) the controller fires signals of the model, by modifying the model. The most important property of the signals and slots mechanism is that objects are not aware of the presence of other objects. In other words, a pushbutton does not need to know anything about the presence of a dialog. The pushbutton just says 'click' to the outside world, and whoever is listening takes action. Signals and slots can be done with C++ templates, but you need one template for each number of parameters that exist. That is why libsig++ or boost have classes like signal0, signal1, signal2, signal3 etc for 0, 1, 2, 3 or more parameters. Objects that have methods which can be slots must inherit from a specific class, which keeps track of which slots belong to which objects and automatically removes the slots from their signals when deleted. Java has anonymous functions: each object (let's say a pushbutton) executes a specific function which usually belongs to some other object (let's say a dialog). Anonymous functions are coded inline at the place of event instantiation. This is an ugly solution, because a class becomes a huge file with functions spreaded here and there. Sun has been critisized for it a lot. C# has the 'event', which is a synonymous to signal. You declare an event like this: event click(int data); And then you add and delete methods to it, like this: click += dialog.accept; This is an elegant solution, but it requires for the destination object to keep track where it has added its methods, so it can remove them when no longer used. What we are asking from you, Walter, is to provide a signal and slot mechanism, that not only reflects the way C# works, but it also allows for automatic removal of slots when an object is deleted, ala C++. If you don't mind, let me give you my view about how the syntax of signals and slots should be: signal Identifier ( Arguments ) ; The signal word should be a keyword. The following operations should be allowed on signals: signal += (delegate); signal -= (delegate); signal(Arguments); The operator += adds a delegate to a signal (only if it has not been added yet, of course); the operator -= removes a delegate from a signal; the operator () calls the signal with the given arguments. Here is the implementation: A signal is a linked list of delegates. Each time operator += is called, a delegate is added to the linked list of delegates the signal contains. Each time operator -= is called, a delegate is removed from the linked list of delegates. When the signal is called, the linked list of delegates is traversed and each delegate is called. Here is the important detail, concering delegates that are methods: The Object class should contain a linked list of pointers to delegates. each time a delegate is added to the signal, a pointer to the delegate is added to the Object-derived object. This pointer is not checked by the garbage collector (it is a weak reference). When the object is deleted, the delegates that the object contains are automatically removed from the signals they belong. In this way, an Object does not have to track in which signals it is registered to. Please, Walter, take this into consideration. I would not have spent so much time, carefully handcrafting this post, if it was not important. D is the best language there is, and the mechanisms are there for the signals and slots mechanism to be implemented(from what I have seen of the dmd sources). It would take you a couple of hours to implement it, but it will save thousands of hours of frustration from D programmers. You say in the documentation that some things are better be part of the language, instead of being done by libraries. You added 'complex' and 'map' (associative array) to the language for this reason. But signals and slots are much more important than either complex and map, I think that they should be part of the language, for elegance...it is one of those mechanisms, that has to be part of D. Don't hesitate to ask me anything about signals and slots...I will be more than happy to answer.

Apr 25 2004
parent J Anderson <REMOVEanderson badmama.com.au> writes:
Colin JN Breame wrote:

Seconded - this is exactly how it should work.  Any idea how we can get Walter
to take a look at this proposal?
  

Put something up on http://www.prowiki.org/wiki4d/wiki.cgi?FeatureRequestList. -- -Anderson: http://badmama.com.au/~anderson/
Apr 25 2004
prev sibling parent Norbert Nemec <Norbert.Nemec gmx.de> writes:
Walter wrote:
 Frankly, I don't understand signals and slots at all.

Signals and Slots are a concept that becomes useful especially in RAD (rapid applcation development): You have many components boxed up and just tie them together to a program. Especially in GUI-development: There you have all kinds of widgets everyone defining some signals and some slots. To put together a program, you just create all the widgets you need, set their properties (color, position, text, etc.) and connect their signals and slots. For writing non-interactive programs, there is little reason to use that concept. For interactive, event-driven programming, though, it is the best invention since sliced bread. The core point that distinguishes the signal/slot concept from plain callbacks is, that the connection is not initiated neither by the sender nor by the receiver. The sender just offers a signal, the receiver offers a slot and the application-programmer connect the two lateron. This is, why it is important to have some mechanism to keep track of all the connections that were created. Nobody really is responsible for connections and still they have to be cleanly removed once one of the parties goes out of business.
Apr 24 2004
prev sibling parent reply Ben Hinkle <bhinkle4 juno.com> writes:
Walter, signals and slots need support at language level. Templates are ugly,
and one has to provide lots of different template classes, with different
names, 
  for each number of parameters.

Signals and slots should follow delegate syntax, using the keyword 'signal', 
like this:

ReturnType signal ( Arguments ) Identifier ;

Signals should contain weak references, as mentioned above, and when the
objects 
that are registered to signals are deleted, slots are automatically deleted.

I can't see what is so hard about removing a delegate that it requires language support. By analogy consider an email majordomo list with a list of subscribers. If someone subscribed to the list wants out (either because they just don't want to get the email anymore or because they are about to ... umm... be deleted and want to put their affairs in order), they unsubscribe from the list. It seems pretty simple. Should the slot be cleared if it asserts, say, 10 times in a row? Just because an object has a weak reference you still don't know when it is no longer in use. There is an unknown period of time between when the object becomes garbage and when it gets collected. In any case what about the delegates that don't have object references but use a stack frame? Should they get automatically deleted, too? The greatest benefit to adding language support would be to avoid templates and provide some syntactic sugar - at least with the implementations presented so far. I don't know all that much about C#'s event keyword but my impression is that it doesn't do the unregistering smarts but it does make it easier to define events and maintain lists of callbacks.
Apr 23 2004
next sibling parent Norbert Nemec <Norbert.Nemec gmx.de> writes:
Ben Hinkle wrote:
 I can't see what is so hard about removing a delegate that it requires
 language support.
 
 By analogy consider an email majordomo list with a list of
 subscribers. If someone subscribed to the list wants out (either
 because they just don't want to get the email anymore or because they
 are about to ... umm... be deleted and want to put their affairs in
 order), they unsubscribe from the list. It seems pretty simple.

I like that analogy! The difficulty I was talking about would be: What if you lost track over what lists you are subscribed to? If, in addition, the messages don't show where they came from, your Mailbox will be flooded and there is little you can do. The problem actually is there, since anybody can create subscriptions, (i.e. connections) Anyhow: your own idea with that .object property would solve the problem, since it would give the mailing list a handle to send a control message to the receiver at subscription time. Now you just collect these control messages and know where you get the messages from.
Apr 24 2004
prev sibling parent reply lgoss007 yahoo.com writes:
Well this thread is kind of old, but I'm somewhat interested in this
signal/slot, event/delegate thing and I happened to find this article on C#
events (which are delegates with a few special constranints/feautures):
http://blog.monstuff.com/archives/000040.html

And here is a simple use of events for non-gui use (a metranome):
http://www.codeproject.com/csharp/SimplestEventExample.asp

I've read that this kind of thing can be done in D, so what would be a simple
way to do this in D (I'm still kind of new to D)?

Thanks,
Lucas
Oct 20 2005
parent reply nick <nick.atamas gmail.com> writes:
lgoss007 yahoo.com wrote:
 Well this thread is kind of old, but I'm somewhat interested in this
 signal/slot, event/delegate thing and I happened to find this article on C#
 events (which are delegates with a few special constranints/feautures):
 http://blog.monstuff.com/archives/000040.html
 
 And here is a simple use of events for non-gui use (a metranome):
 http://www.codeproject.com/csharp/SimplestEventExample.asp
 
 I've read that this kind of thing can be done in D, so what would be a simple
 way to do this in D (I'm still kind of new to D)?
 
 Thanks,
 Lucas
 
 

Start here: http://www.digitalmars.com/d/type.html#delegates search for the word delegate in this page for some examples: http://www.digitalmars.com/d/expression.html also on this page: http://www.digitalmars.com/d/statement.html and just try it things with DMD until it works. Goodluck
Feb 08 2006
parent diablito bk.ru writes:
Like this

Module ss.d

typedef void delegate(...)  SIGNAL;
typedef void delegate(...)  SLOT;

struct SIGNAL_SLOT
{
SIGNAL _in;
SLOT _out;
}

struct SIGNAL_SIGNAL
{
SIGNAL _in;
SIGNAL _out;
}

extern (D) SIGNAL_SLOT[int] signal_slot;
extern (D) SIGNAL_SIGNAL[int] signal_signal;


static class SS
{
//**************************************************************************
static void connect(SIGNAL _in, SLOT _out)
{
int index = -1;
int keys[] = signal_slot.keys;

foreach (int key; keys)
{
if (signal_slot[key]._in==_in && signal_slot[key]._out==_out )
{
index = key;
break;
}
}

if (index==-1)
{
SIGNAL_SLOT tmp;
tmp._in = _in;
tmp._out = _out;
signal_slot[signal_slot.length+1] = tmp;
}

}


//**************************************************************************
static void connect2(SIGNAL _in, SIGNAL _out)
{
if (_in==_out){return;}

int index = -1;
int keys[] = signal_signal.keys;

foreach (int key; keys)
{
if (signal_signal[key]._in==_in && signal_signal[key]._out==_out )
{
index = key;
break;
}
}

if (index==-1)
{
SIGNAL_SIGNAL tmp;
tmp._in = _in;
tmp._out = _out;
signal_signal[signal_signal.length+1] = tmp;
}
}


//**************************************************************************
static void disconnect(SIGNAL _in, SLOT _out)
{
int keys[] = signal_slot.keys;

foreach (int key; keys)
{
if (signal_slot[key]._in==_in && signal_slot[key]._out==_out )
{
signal_slot.remove(key);
break;
}
}
}

//**************************************************************************
static void disconnect2(SIGNAL _in, SIGNAL _out)
{
int keys[] = signal_signal.keys;

foreach (int key; keys)
{
if (signal_signal[key]._in==_in && signal_signal[key]._out==_out )
{
signal_signal.remove(key);
break;
}
}
}


//**************************************************************************
static void signal(SIGNAL _in, void *_argptr, TypeInfo[] _arguments)
{
int keys[] = signal_slot.keys;
foreach (int key; keys)
{
if (signal_slot[key]._in==_in )
{
signal_slot[key]._out(_argptr,_arguments);
}
}

int keys2[] = signal_signal.keys;
foreach (int key; keys2)
{
if (signal_signal[key]._in==_in )
{
//signal_signal[key]._out(_argptr,_arguments);
}
}
}


}



How use

import ss;


class test
{
this(){}
~this(){}


void signal(...)
{
printf("signal\n");
SS.signal(&this.signal,_argptr,_arguments);

}

void signal2(...)
{
printf("signal2\n");
SS.signal(&this.signal2,_argptr,_arguments);
}



void slot(...)
{
printf("slot\n");
}

void slot2(...)
{
printf("slot2\n");
}
}


void f(int f)
{
test t = new test();

SS.connect(&t.signal, &t.slot);
SS.connect(&t.signal2, &t.slot2);
SS.connect(&t.signal, &t.signal2);
t.signal();

delete t;
}
Jul 16 2006