After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 473804 - Syntax for signals with emiter.
Syntax for signals with emiter.
Status: RESOLVED WONTFIX
Product: vala
Classification: Core
Component: general
0.1.x
Other All
: Normal minor
: ---
Assigned To: Vala maintainers
Vala maintainers
: 509736 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2007-09-05 06:42 UTC by Mathias Hasselmann (IRC: tbf)
Modified: 2010-08-03 08:31 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description Mathias Hasselmann (IRC: tbf) 2007-09-05 06:42:04 UTC
Currently Vala uses this syntax to mark signals with emitter like GObject::notify:

public class Object {
    [HasEmitter]
    public signal void notify(string! property_name);
}

Notice how a wrong signature ("string! property_name" instead of "ParamSpec! property") is used, to allow invokation of the emitter. This causes problems with lambda expressions:

    object.notify += (o, p) {
        stdout.printf ("property `%s' has changed\n", p.name);
    }

Is rejected: The name `name' does not exist in the context of `string'.

Suggesting new syntax for signals with emitters. It was designed to be in line of C# methods which can have explicit "add" and "remove" methods:

public class Object {
    public signal void notify(ParamSpec! property) {
        void emit(string! property_name);
    }
}

Optionally we could extend it to this, to match the feature set of C#:

public class Object {
    public signal void notify(ParamSpec! property) {
        void emit(string! property_name);

        add { 
            do_something ();

            Signal.connect_closure (this, "notify", value, false);
        }

        remove { 
            do_something ();

            Signal.disconnect (this, value);
        }
    }
}
Comment 1 Jürg Billeter 2007-09-05 07:06:30 UTC
That looks like an interesting solution. I don't think that we should support add/remove as that's not common in GObject libraries and I also don't see a need for it.

Something else we should support is to have a vfunc as default signal handler and a nice syntax to override that default handler in derived classes.

We probably also need to support at least the FIRST/LAST/DETAILED signal flags and the AFTER connect flag.
Comment 2 Mathias Hasselmann (IRC: tbf) 2007-09-05 07:50:34 UTC
(In reply to comment #1)
> That looks like an interesting solution. I don't think that we should support
> add/remove as that's not common in GObject libraries and I also don't see a
> need for it.

Yes agreed. Just added them for completeness. We should not add this, unless someone comes up with an use-case.

> Something else we should support is to have a vfunc as default signal handler
> and a nice syntax to override that default handler in derived classes.

Thought it would be easy:

public class Object {
    public signal void notify(ParamSpec! property) {
        virtual void emit(string! property_name) {
        }
    }
}

for declaration and:

public class Maman.Bar : Object {
    public signal void notify(ParamSpec! property) {
        override void emit(string! property_name) {
        }
    }
}

for override, when I remembered the initial purpose of this ticket: Conflicting signatures. So what about this:

public class Object {
    public virtual signal void notify(ParamSpec! property) {
        default { // NOTICE: use "default" to avoid new keyword
        }
    }
}

public class Maman.Bar : Object {
    public override signal void notify(ParamSpec! property) {
        default { // NOTICE: use "default" to avoid new keyword
        }
    }
}

Or:

public class Object {
    public signal void notify(ParamSpec! property) {
        virtual default { // NOTICE: use "default" to avoid new keyword
        }
    }
}

public class Maman.Bar : Object {
    public signal void notify(ParamSpec! property) {
        override default { // NOTICE: use "default" to avoid new keyword
        }
    }
}

Difference is in position of the virtual/override modifier. 
Do we even need that modifier - expect for consistence?

> We probably also need to support at least the FIRST/LAST/DETAILED signal flags

Without new keywords:

    [SignalFlags.RUN_FIRST | SignalFlags.DETAILED]
    public signal void notify(ParamSpec! property);

or more redundant, but consistent with current attribute usage:

    [SignalFlags(SignalFlags.RUN_FIRST | SignalFlags.DETAILED)]
    public signal void notify(ParamSpec! property);

or with absolutly smart parsing, which changes identifier scope, when detecting the attribute actually being a flag:

    [SignalFlags(RUN_FIRST, DETAILED)]
    public signal void notify(ParamSpec! property);

Alternativly signals would be avoided by something like this:

    public signal void notify(ParamSpec! property) {
        flags = SignalFlags.RUN_FIRST | SignalFlags.DETAILED;
    }

> and the AFTER connect flag.

I still thinks this should be implemented:

    signal_name["detail_name"].flag += ....

Alternativly:

    signal_name["detail_name", flags] += ....


Guess I even prefer this second variant, which should looks more pretty to me and should be easy to implement. Specially when allowing to drop the flag's typename:

    notify["bar", AFTER | SWAPPED] += ....

or (would make the code generator even more trivial):

    notify["bar", AFTER, SWAPPED] += ....
    notify[AFTER "bar", SWAPPED,] += ....
    notify[AFTER, SWAPPED, "bar"] += ....

Code would be like this:

    foreach (Expression index in expr.indices) {
        if (index is StringLiteral) {
            if (null == detail) {
                detail = ((StringLiteral)index).value;
            }
                Report.error (...);
            }
        } else if (index.static_type.data_type.is_subtypeof (connect_flags_type)) {
            ....
            cflags = new CCodeBinaryExpression (CCodeBinaryOperator.OR, cflags, index.ccodenode);
            ....
        }
    }
Comment 3 Jürg Billeter 2007-09-05 09:42:21 UTC
Using the default keyword might make sense. As the default handler is the only thing you can override, I wonder whether we should just use

    public override signal void notify(ParamSpec! property) {
        ...
    }

in derived classes. Don't know whether that syntax combination would make sense, though.

I don't think we should ever expose SWAPPED as we don't expose user_data. Something like

    notify["bar"].after += ...

makes more sense, IMO.

(after being a member of all signals, not a keyword)
Comment 4 Mathias Hasselmann (IRC: tbf) 2007-09-05 10:10:21 UTC
(In reply to comment #3)
> Using the default keyword might make sense. As the default handler is the only
> thing you can override, I wonder whether we should just use
> 
>     public override signal void notify(ParamSpec! property) {
>         ...
>     }
> 
> in derived classes. Don't know whether that syntax combination would make
> sense, though.

Looks convenient, as it requires less code, but:

 - violates the principle of minimal surprise, as this syntax differs from the syntax for base classes
 - closes the door for overriding other signal related methods, like emit or add/remove (when ever added)

> I don't think we should ever expose SWAPPED as we don't expose user_data.

Exposing swapped makes sense for attaching static methods and for directly attaching the current instances hide methode to some button's click signal:

   button.click.swapped += hide;

Without SWAPPED the button would disappear on click.
Comment 5 Jürg Billeter 2007-09-05 11:21:29 UTC
(In reply to comment #4)
>  - violates the principle of minimal surprise, as this syntax differs from the
> syntax for base classes

Yes, that is also my concern. I'd like to get overriding a default signal handler easy as it's probably more common than using a custom emitter, for example when deriving a Widget.

>  - closes the door for overriding other signal related methods, like emit or
> add/remove (when ever added)

I haven't seen any virtual emit function yet, we probably don't need to support that. I'm not really conerned about add/remove.

> > I don't think we should ever expose SWAPPED as we don't expose user_data.
> 
> Exposing swapped makes sense for attaching static methods and for directly
> attaching the current instances hide methode to some button's click signal:
> 
>    button.click.swapped += hide;
> 
> Without SWAPPED the button would disappear on click.

That should be handled internally by Vala, in my opinion. I consider the position of the self/this, sender, and user_data parameters as implementation details. Your SWAPPED example only works for the special case of parameter-less methods/signals.
Comment 6 Mathias Hasselmann (IRC: tbf) 2007-09-05 11:56:51 UTC
(In reply to comment #5)
> > Exposing swapped makes sense for attaching static methods and for directly
> > attaching the current instances hide methode to some button's click signal:
> > 
> >    button.click.swapped += hide;
> > 
> > Without SWAPPED the button would disappear on click.
> 
> That should be handled internally by Vala, in my opinion. I consider the
> position of the self/this, sender, and user_data parameters as implementation
> details. Your SWAPPED example only works for the special case of parameter-less
> methods/signals.

I was going to saying that without a crystal ball you cannot know that. Well but considering the semantics of

    button.click += this.button_click_cb;

it is absolutly clear, that
 
    button.click += this.hide;

shall call the hide method of the current widget. To connect to the button's hide method you'd write:

    button.click += button.hide;
Comment 7 Jürg Billeter 2008-01-17 15:38:16 UTC
*** Bug 509736 has been marked as a duplicate of this bug. ***
Comment 8 Jürg Billeter 2008-01-25 15:49:05 UTC
I'm currently tending to the following proposal.

Default handlers:

public class Object {
    public virtual signal void notify(ParamSpec! property) {
        default {
        }
    }
}

public class Maman.Bar : Object {
    public override signal void notify(ParamSpec! property) {
        default {
        }
    }
}

Emitters:

public class Object {
    public signal void notify(ParamSpec! property) { emit; }
}

Signals declared without "emit" may only be emitted from methods in the same class.
Comment 9 Maciej (Matthew) Piechotka 2008-01-25 16:11:06 UTC
notify signal emitter gets a string as far as I know.
Isn't better to have something like:
public class Object {
    public signal void notify(ParamSpec! property) { emit(String! property); }
}
Comment 10 Jürg Billeter 2008-05-28 23:31:04 UTC
2008-05-29  Jürg Billeter  <j@bitron.ch>

	* vala/valamethod.vala:
	* vala/valaparser.vala:
	* vala/valasignal.vala:

	Add support for overriding default method handlers of signals
	that have been declared with the `virtual' modifier
Comment 11 rainwoodman 2009-03-06 18:12:17 UTC
Bug 571685 is related to this one.

Notice that in vala 0.5.7, even if a signal is declared as virtual, no corresponding vfunc is ever created, and no default handler is set in g_signal_new.



Comment 12 Jürg Billeter 2010-08-03 08:31:42 UTC
Handling of virtual signals has been fixed in the meantime. I'm not planning to add explicit emitter in the foreseeable future.