On Monday, 5 November 2012 at 13:35:26 UTC, Robert wrote:
Hi there!

I just developed a proof-of-concept implementation of an improved
std.signals.

Things I did not like about the current implementation:

1. Passing a delegate to connect, which basically is an extended (void*) and assuming it is an object delegate, does not feel quite right in D, where we usually try to guarantee correctness by the compiler wherever
possible.

2. The restriction that only objects can be connected.

Point 2 does not really bother me, because from my little experience I never really had connected anything else than objects to a signal. But the restriction that the connected delegate must not be some wrapper, is quite a restriction I came to dislike about Qt and even more so with this implementation because unlike Qt the signatures have to match
exactly, you can't omit parameters, like:
// Qt code
connect(button, SIGNAL(clicked(bool)), this, SLOT(buttonClicked());

-> In the current implementation buttonClicked would have to take a
bool.

In addition boost::signals together with boost::bind offered even more comfort like passing additional parameters to a slot, which is really
very, very useful:

for(int i=0; i<buttons.length(); i++) {
        buttons[i].clicked.connect(boost::bind(&MyObj::addNumber, this,
 i));
}

So I tried to improve std.signals, code:
(warning at least one bug is remaining, explained below)

https://github.com/eskimor/phobos/tree/new_signal

You can easily connect to an object's method with:

obj.signal.connect!"someMethod"(obj);

instead of the old implementation:

obj.signal.connect(&obj.someMethod);

-> The interface is clean and type safe, all the ugly details are hidden from the user. And it is just one character more typing. Simple things
stay simple.

In addition a method allowing wrapper delegates was added:

class Observer {
        void addNumber(int i) {
                sum+=i;
        }
        int sum;
}

class Button {
        Signal!(bool) clicked;
        // ...
}

void main() {
        auto b=new Button;
        auto o=new Observer;
        // Ignore boolean parameter and pass some int:
        b.connect!Observer(o, (o, p) { o.addNumber(7); });
        // or even:
        b.connect!Observer(o, (o, p) => o.addNumber(7));
        // For some reason the compiler is not able to deduce "o" being
        // Observer, so the !Observer is needed, but it is still very
        // neat and readable.
}

Thanks to D's lamdas the syntax is even more concise as boost::bind and
far more powerful.

By passing the object explicitly to the delegate, it is possible to maintain the 'weak ref' semantics to the target object, while ensuring
that the delegates context won't be freed.

As a side effect it is now even possible to use struct delegates or even any non object delegate. Simply pass null for the obj parameter. It is completely safe, the only drawback is that the struct won't be deleted until the Button gets destroyed. (Because it holds a reference to the struct, by means of the delegate.) But for long lived structs this
usually is perfectly acceptable.

Implementation:

In my implementation I changed the Signal mixin to be a simple template struct, because I hit strange compiler errors with it being a mixin. The
most prominent:

std/signals.d(443): Error: no overload matches for connect(string
method,T2) if (is(T2 : Object))

You can find the version triggering these errors at:

https://github.com/eskimor/phobos/tree/new_signal_mixin

Also I did not really get the point why a mixin was used in the first place, it does not really gain us anything? What was the reasoning about
it?
I almost thought I found the reason, because my implementations suffers from unhook not being called, although it was properly registered with "rt_attachDisposeEvent(obj, &unhook);", thus causing a segmentation
fault when the observer gets deleted. I did not really find any
difference from the original version that could explain this behavior, despite the original implementation being a mixin. So I thought, well maybe the delegate passed to "rt_attachDisposeEvent(obj, &unhook);" must be an object delegate (that's would be why the mixin was needed), but after digging in object_.d I did not find any code assuming that the
delegate was an object delegate. Any ideas on this?

Another thing I'd like to ask Walter, is what the "L1:" label is for in connect(), is it just some left over or has it some special internal
compiler thing meaning?

What do you think?

Best regards,

Robert

Hi!Could you write some examples for struct and non-object delegates?

Reply via email to