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


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

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


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

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

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





April 24, 2004
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.
April 25, 2004
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.
>  
>
I think signals and slots could be implemented neatly in a library if some language support was provided for removing delegates that reference objects that no-longer exist.

-- 
-Anderson: http://badmama.com.au/~anderson/
April 25, 2004
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
>> 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.
>>
>> 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.
>
>
>
>
>