Jump to page: 1 24  
Page
Thread overview
Signal/Slot mechanism?
Apr 23, 2004
Norbert Nemec
Apr 23, 2004
J Anderson
Apr 23, 2004
Norbert Nemec
Apr 23, 2004
Bastiaan Veelo
Apr 23, 2004
Stephan Wienczny
Apr 23, 2004
Norbert Nemec
Apr 23, 2004
Norbert Nemec
Apr 23, 2004
J Anderson
Apr 23, 2004
Bastiaan Veelo
Apr 23, 2004
Ben Hinkle
Apr 23, 2004
Norbert Nemec
Apr 23, 2004
Colin JN Breame
Apr 23, 2004
J Anderson
Apr 23, 2004
Bastiaan Veelo
Apr 23, 2004
Andy Friesen
Apr 23, 2004
Bastiaan Veelo
Apr 23, 2004
Ben Hinkle
Apr 23, 2004
Norbert Nemec
Apr 23, 2004
Andy Friesen
Apr 23, 2004
Norbert Nemec
Apr 23, 2004
Walter
Apr 25, 2004
J Anderson
Apr 25, 2004
Norbert Nemec
Apr 25, 2004
Norbert Nemec
Apr 25, 2004
Colin JN Breame
Apr 25, 2004
J Anderson
Apr 24, 2004
Norbert Nemec
Apr 24, 2004
Ben Hinkle
Apr 24, 2004
Norbert Nemec
Oct 20, 2005
lgoss007
Feb 09, 2006
nick
Jul 17, 2006
diablito
April 23, 2004
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
April 23, 2004
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/
April 23, 2004
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.

April 23, 2004
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

April 23, 2004
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?
April 23, 2004
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.

April 23, 2004
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.
April 23, 2004
"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
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

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);


April 23, 2004
[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
[snip]

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;
}
April 23, 2004
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);
>  
>
This is basicly the dig thing I posted before.

-- 
-Anderson: http://badmama.com.au/~anderson/
« First   ‹ Prev
1 2 3 4