|
Archives
D Programming
D
D.gnu
digitalmars.D
digitalmars.D.bugs
digitalmars.D.dtl
digitalmars.D.dwt
digitalmars.D.announce
digitalmars.D.learn
digitalmars.D.debugger
C/C++ Programming
c++
c++.announce
c++.atl
c++.beta
c++.chat
c++.command-line
c++.dos
c++.dos.16-bits
c++.dos.32-bits
c++.idde
c++.mfc
c++.rtl
c++.stl
c++.stl.hp
c++.stl.port
c++.stl.sgi
c++.stlsoft
c++.windows
c++.windows.16-bits
c++.windows.32-bits
c++.wxwindows
digitalmars.empire
digitalmars.DMDScript
|
D - Signal/Slot mechanism?
↑ ↓ ← → 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
↑ ↓ ← → 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/
↑ ↓ ← → 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.
↑ ↓ ← → 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
↑ ↓ ← → 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?
↑ ↓ ← → 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.
↑ ↓ ← → 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.
↑ ↓ ← → "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);
↑ ↓ ← → 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/
↑ ↓ ← → 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.
↑ ↓ ← → "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
↑ ↓ ← → 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);
↑ ↓ ← → 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);
↑ ↓ ← → 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/
↑ ↓ ← → 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.
↑ ↓ ← → 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
↑ ↓ ← → 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.
↑ ↓ ← → 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.
↑ ↓ ← → 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;
}
↑ ↓ ← → 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!)
↑ ↓ ← → 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
↑ ↓ ← → 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.
↑ ↓ ← → 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.
↑ ↓ ← → "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.
↑ ↓ ← → "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.
↑ ↓ ← → 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/
↑ ↓ ← → "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.
↑ ↓ ← → 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)
↑ ↓ ← → "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.
↑ ↓ ← → 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?
↑ ↓ ← → 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.
↑ ↓ ← → 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/
↑ ↓ ← → 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.
↑ ↓ ← → 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.
↑ ↓ ← → 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.
↑ ↓ ← → 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
↑ ↓ ← → 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
↑ ↓ ← → 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;
}
|
|