- Code: Select all
# A Rat is like a Mouse but bigger and hairier with a longer naked tail
sig RatEventHandler(sender as Object, args as RatEventArgs) # exists - signature for an eventHandler
# event <eventId> as <sigId> #-- new syntax for event?
event ratEvent as RatEventHandler
def onRatEvent( args asRatEventArgs) # fireRatEvent invoking handlers/Observers
# lowlvl detail like C#
h as RatEventHandler? = RatEvent
if h
raise h, this, args
# OR hide detail handling - statementish
# raise <event>, <eventHandler commaSep argList>
raise ratEvent, this, args # trailing expr after eventname are arg vals to pass to handler/delegates
# OR functionish
# raise <eventId>( <eventHandler commaSep argList>)
raise ratEvent(this, args)
Forums
Event declarations and firing Events
23 posts
• Page 1 of 3 • 1, 2, 3
Event declarations and firing Events
Did you have any syntax in mind for Event declaration and for firing/Raising Events ?
- hopscc
- Posts: 632
- Location: New Plymouth, Taranaki, New Zealand
Re: Event declarations and firing Events
Yes, in fact I have a workspace with outstanding changes for this that I need to finish up. I also like the conveniences that Visual Basic (VB) provides in this area such as "handles ...". Occasionally, VB does something more conveniently than C#. But the initial Cobra version will be very similar to C#.
One difference with C# is that instead of "if someEvent, someEvent(args)" you'll write "raise someEvent(args)" as you speculated in your message. The nil check will be implicit.
It is interesting to note that since only an object can raise its own events, the first arg is always this. Like the "if someEvent" it therefore becomes somewhat redundant. So just thinking out loud here:
I'm starting to like that. It's clean and the implied semantics are simple and consistent (always check for nil, always pass this, ...).
-Chuck
One difference with C# is that instead of "if someEvent, someEvent(args)" you'll write "raise someEvent(args)" as you speculated in your message. The nil check will be implicit.
It is interesting to note that since only an object can raise its own events, the first arg is always this. Like the "if someEvent" it therefore becomes somewhat redundant. So just thinking out loud here:
- Code: Select all
raise .clicked, EventArgs()
# furthermore, if the argument types have default constructors and
# those are satisfactory to you:
raise .clicked
I'm starting to like that. It's clean and the implied semantics are simple and consistent (always check for nil, always pass this, ...).
-Chuck
- Charles
- Posts: 2515
- Location: Los Angeles, CA
Re: Event declarations and firing Events
What if you need/want to fake out the receivers (Observers/handlers)
The event is always raised by Object X but the receivers need to get another object e.g. X.container? (contrived to be sure but sometime necessary)
I'd suggest you allow full specification of the 2 args as an option at least..
Looking at the syntax possibilities I'm quite enamoured with the functionish version
raise eventName( sender, eventArgs) # or raise eventName or raise eventName( eventArgs)
It just seems tidier ( probably cos just looks more familiar..)
The event is always raised by Object X but the receivers need to get another object e.g. X.container? (contrived to be sure but sometime necessary)
I'd suggest you allow full specification of the 2 args as an option at least..
Looking at the syntax possibilities I'm quite enamoured with the functionish version
raise eventName( sender, eventArgs) # or raise eventName or raise eventName( eventArgs)
It just seems tidier ( probably cos just looks more familiar..)
- hopscc
- Posts: 632
- Location: New Plymouth, Taranaki, New Zealand
Re: Event declarations and firing Events
So then we'd have:
Regarding implementation for the last 2 cases where the call is one argument short, the compiler would try:
(1) with "this" prepended to the argument list, and
(2) with the last argument default instance appended to the argument list
If exactly one works, it's good. If both or neither work, an error.
In practice, event argument classes inherit from EventArgs and the classes that raise events do not. So it shouldn't be a problem.
I really don't want to pass "this", but I understand your concern that you may need to pass something on occasion.
I have mixed feelings about using the "call/parens" style. The "listen" and "ignore" statements (already implemented) are comma separated as well as other keyword statements such as "assert", "print" and "trace":
And it might be easier to explain the special treatment of arguments for a "raise" statement than a call.
-Chuck
raise .clicked
# this and EventArgs() assumed
raise .clicked(this)
# EventArgs() assumed
raise .clicked(this, EventArgs())
# full specification
raise .clicked(.container)
# specifying a fake sender; EventArgs() assumed
raise .clicked(EventArgs())
# this assumed
Regarding implementation for the last 2 cases where the call is one argument short, the compiler would try:
(1) with "this" prepended to the argument list, and
(2) with the last argument default instance appended to the argument list
If exactly one works, it's good. If both or neither work, an error.
In practice, event argument classes inherit from EventArgs and the classes that raise events do not. So it shouldn't be a problem.
I really don't want to pass "this", but I understand your concern that you may need to pass something on occasion.
I have mixed feelings about using the "call/parens" style. The "listen" and "ignore" statements (already implemented) are comma separated as well as other keyword statements such as "assert", "print" and "trace":
listen saveButton.onClick, ref .save
ignore saveButton.onClick, ref .save
print x, y
trace a, b
assert x < y, z
raise .clicked
# this and EventArgs() assumed
raise .clicked, this
# EventArgs() assumed
raise .clicked, this, EventArgs()
# full specification
raise .clicked, .container
# specifying a fake sender; EventArgs() assumed
raise .clicked, EventArgs()
# this assumed
# most frequent cases will be these two:
raise .something
raise .something, MyEventArgs(a, b)
And it might be easier to explain the special treatment of arguments for a "raise" statement than a call.
-Chuck
- Charles
- Posts: 2515
- Location: Los Angeles, CA
Re: Event declarations and firing Events
Not exactly. Too complex. Thinking we'd have 3 cases Only
The shortened forms cover the most common cases (?) and the full form is there if need to do something uncommon ( explicitly)
Re the call/parens style:
I agree that the already implemented keyword statements use comma sep lists is a really good reason not to do this.
( that plus I've spent most of today (its cold and rainy out) doing an implementation of this
- the keyword + comma sep list implemented easily and rapidly,
the parens/fn call style has not - lotsof weird little failure points sprinkled all over and ugly casting which leads me to believe I'm pushing against the flow)
I still think it reads better but can live with the alternative
I think in writing your own Observables theres usually 2 main Observer cases:
1) Only interested that something happened - pure event no state ( null Event Handler)
2) Want some indication of Event state - EventArgs subclassed, fields added for specific info ...
In any case when there is state being supplied I think it needs to be explicitly shown i.e never assumed - even if its just a non subclassed EventArgs
( in that case you'd still need an explicit EventArgs construct call to populate it with the specific values you want to pass on - If you're passing a
novalue EventArgs ( EventArgs() ) You're doing the equivalent of a no state event anyway...)
- Code: Select all
# no args
raise .clicked
# no this and EventArgs() at all ( null arg EventHandler)
# 1 arg = explicit eventArgs
raise .clicked(EventArgs())
# this assumed
#Otherwise full spec
raise .clicked(this, EventArgs())
# full specification
# raise .clicked(.container, EventArgs() )
# specifying a fake sender; full Specification EventArgs() explicit
The shortened forms cover the most common cases (?) and the full form is there if need to do something uncommon ( explicitly)
Re the call/parens style:
I agree that the already implemented keyword statements use comma sep lists is a really good reason not to do this.
( that plus I've spent most of today (its cold and rainy out) doing an implementation of this
- the keyword + comma sep list implemented easily and rapidly,
the parens/fn call style has not - lotsof weird little failure points sprinkled all over and ugly casting which leads me to believe I'm pushing against the flow)
I still think it reads better but can live with the alternative
- Code: Select all
raise .clicked
# No this and EventArgs() - null EventHandler
# 1 arg = No this
raise .clicked, EventArgs()
# this assumed
raise .clicked, this, EventArgs()
# full specification
raise .clicked, .container, EventArgs()
# specifying a fake sender; EventArgs() explicit
raise .clicked, this
# Invalid sorta - this passed to args - becomes same as raise .clicked, this, this
I think in writing your own Observables theres usually 2 main Observer cases:
1) Only interested that something happened - pure event no state ( null Event Handler)
2) Want some indication of Event state - EventArgs subclassed, fields added for specific info ...
In any case when there is state being supplied I think it needs to be explicitly shown i.e never assumed - even if its just a non subclassed EventArgs
( in that case you'd still need an explicit EventArgs construct call to populate it with the specific values you want to pass on - If you're passing a
novalue EventArgs ( EventArgs() ) You're doing the equivalent of a no state event anyway...)
- hopscc
- Posts: 632
- Location: New Plymouth, Taranaki, New Zealand
Re: Event declarations and firing Events
My desire to imply EventArgs() instead of nil has to do with my aversion to nil references which lead to run-time exceptions. Also, if the event args class for that event signature has a default constructor then it's perfectly reasonable to use it. And if it doesn't then it's usually the case that you really need to explicitly create an instance. My experience in C# is that it would be rare to have a MyEventArgs(a, b) and not want to pass the "a" and "b" information along.
Also, for event signatures declared in Cobra, an argument such as "args as MyEventArgs" defaults to non-nil. You'd have to put a ? on it to accept nil.
Finally, I just checked in initial support for declaring and raising events. No support for default arguments yet. Example Implementation
Also, for event signatures declared in Cobra, an argument such as "args as MyEventArgs" defaults to non-nil. You'd have to put a ? on it to accept nil.
Finally, I just checked in initial support for declaring and raising events. No support for default arguments yet. Example Implementation
- Charles
- Posts: 2515
- Location: Los Angeles, CA
Re: Event declarations and firing Events
I dont understand what you mean by 'imply EventArgs instead of nil' or where the nil discussion is coming from.
None of the 3 cases pass nil around.
The first case is for a Null (arg) EventHandler - a method that takes no args. When the event is fired it passes no args.
Its the degenerate case for Pure event notification (no state)
My other point was that if you are designing the event to pass state then it should have to be explicitly specified ( at the raising end)- not assumed and inserted by the compiler (even if its an empty/default constructor or a unaugmented EventArgs(). This way its always explicit and obvious on code reading what the state info to be sent is. The other is a rich source of obscurely wrong behaviour
None of the 3 cases pass nil around.
The first case is for a Null (arg) EventHandler - a method that takes no args. When the event is fired it passes no args.
Its the degenerate case for Pure event notification (no state)
- Code: Select all
sig NullEventHandler
...
event anEvent as NullEventHandler
def onAnEvent
# this in cobra even tho it will be elided into gen c# code
NullEventHandler h = .anEvent
if h
h() # Null event handler invocation no args sent
...
def getAnEvent
print 'Got an event - no args'
...
listen .anEvent , ref .getAnEvent
My other point was that if you are designing the event to pass state then it should have to be explicitly specified ( at the raising end)- not assumed and inserted by the compiler (even if its an empty/default constructor or a unaugmented EventArgs(). This way its always explicit and obvious on code reading what the state info to be sent is. The other is a rich source of obscurely wrong behaviour
- hopscc
- Posts: 632
- Location: New Plymouth, Taranaki, New Zealand
Re: Event declarations and firing Events
Coupla little buglets in that Example implementation
-emitted C# code for the Event doesnt actually have the 'event 'modifier.
-calls to raise on successive lines gives a parse error about 'expecting EOL and getting raise'
need a .undo after call commaSepExprs in raiseStmt
The raise implementation doesnt work for events declared with leading underscore names (protected)
was that intentional?
- recognise IdentifierExpr in RaiseStmt._bindImp, set DefaultAccessLevel to 'protected' if underscore name in BoxEvent
-emitted C# code for the Event doesnt actually have the 'event 'modifier.
-calls to raise on successive lines gives a parse error about 'expecting EOL and getting raise'
need a .undo after call commaSepExprs in raiseStmt
The raise implementation doesnt work for events declared with leading underscore names (protected)
was that intentional?
- recognise IdentifierExpr in RaiseStmt._bindImp, set DefaultAccessLevel to 'protected' if underscore name in BoxEvent
- Attachments
-
- raiseEx.patch
- (1.84 KiB) Downloaded 552 times
- hopscc
- Posts: 632
- Location: New Plymouth, Taranaki, New Zealand
Re: Event declarations and firing Events
This form of event raising is a race condition in c# in a multithread environment evidently
handler added or removed between test and invocation
reccomended pattern is
For cobra to do that needs to capture the delegate signature and emit in into event raising code.
Uploaded patch includes above plus mod for raise code emission plus protected event test case.
- Code: Select all
if (event != null) { event(this, args);
handler added or removed between test and invocation
reccomended pattern is
- Code: Select all
eventType hdlr = event;
if (hdlr != null)
hdlr(this, args);
For cobra to do that needs to capture the delegate signature and emit in into event raising code.
Uploaded patch includes above plus mod for raise code emission plus protected event test case.
- Attachments
-
- raiseEx1.patch
- (4.23 KiB) Downloaded 552 times
- hopscc
- Posts: 632
- Location: New Plymouth, Taranaki, New Zealand
Re: Event declarations and firing Events
I've read about that, but always wondered if it was really true. Doesn't the assignment just assign a reference to the event? In that case, the event, which is backed by a multicast delegate, is still running through the same list of targets which could be modified by their handlers.
Does your test case actually fail if the local var assignment is missing?
Does your test case actually fail if the local var assignment is missing?
- Charles
- Posts: 2515
- Location: Los Angeles, CA
23 posts
• Page 1 of 3 • 1, 2, 3
Who is online
Users browsing this forum: No registered users and 6 guests