std.signals2 proposal

Ellery Newcomer ellery-newcomer at utulsa.edu
Mon Nov 5 20:22:43 PST 2012



On 11/05/2012 05:36 AM, Robert wrote:
> I just developed a proof-of-concept implementation of an improved
> std.signals.

Cool.

> 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.

Not sure I understand why such hatred is rational?

> Point 2 does not really bother me,

It bothers me.

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

in emit:

if(slot.indirect.ptr) {
  <stuff>
}else{
  <otherstuff>
  slot.indirect.ptr = <thing>
  slot.indirect(i);
}

<stuff> will not be executed more than once?

in addSlot:

you use malloc &friends to allocate objs, but the gc to allocate slots? 
The correct thing to do here is use allocators, which we will totally 
have fleshed out by Christmas (right, Andrei?).

in connect [Object]:

<Tony Shalhoub voice> It's not formatted right! </Tony Shalhoub voice>

You might s/is(T2 : Object)/is(T2 == class)/

Also, it looks like it will not delegate to any overriding methods, but 
I don't know if that is desired. I doubt it differs from existing 
std.signal.

invariant:

> 	assert(slots_idx==slots.length); // I probably even remove slots_idx all together.

Yes you should. Also, you should assert that slots.length == objs.length.

private:
> union DelegateTypes
> {
> void delegate(void*, T1) indirect;
> void delegate(T1) direct;
> }

Could you explain why indirect must have its first param void* ?

>
> 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 improvement is where?

> 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.
> }

Nice.
Should that be b.clicked.connect?

>
> 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.

When would the delegate's context be freed? I think it wouldn't.

>
> 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.

I like this capability, but the api could handle the case of non-object 
delegates better.

<nitpick> Not completely safe if the struct was allocated on the stack 
</nitpick>

> 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:

Welcome to D.

> 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?

So you can do b.connect in the simplest case?

>
> What do you think?

I like the way this is headed.


More information about the Digitalmars-d mailing list