www.digitalmars.com         C & C++   DMDScript  

D - event handling: request for comments

reply "Pavel Minayev" <evilone omen.ru> writes:
This is something I would really like to hear others' opinions
about: event handling in my GUI library, WinD. Currently, it
tries to mimic the Borland's approach (pointers to methods),
and does it by some rather dirty hacks, like modifying the
vtable at run-time, besides, it's highly error-prone... Needless
to say, I'm not very satisfied with this solution. Should it
be dropped in favor of other method, and if so, which? I only
have one idea: a single handler for ALL events from this and
child controls:

    class MyForm: Form
    {
        TextBox txtName;
        Button cmdOk;

        ...

        void eventHandler(Event e)
        {
            switch (e.sender)
            {
            case txtName:
                switch (e.type)
                {
                case Event.Click:
                    ...
                case Event.Change:
                    ...
                }
                break;
            case cmdOk:
                if (e.type == Event.Click)
                    ...
            }
        }
    }

This method is very simple to implement, but it results in code hard to
write, read and understand, and limits the enchancement capabilities
(adding new events could be a problem).


Other ideas?
Feb 23 2002
next sibling parent reply "Robert W. Cunningham" <rwc_2001 yahoo.com> writes:
Pavel Minayev wrote:

 This is something I would really like to hear others' opinions
 about: event handling in my GUI library, WinD. Currently, it
 tries to mimic the Borland's approach (pointers to methods),
 and does it by some rather dirty hacks, like modifying the
 vtable at run-time, besides, it's highly error-prone... Needless
 to say, I'm not very satisfied with this solution. Should it
 be dropped in favor of other method, and if so, which? I only
 have one idea: a single handler for ALL events from this and
 child controls:
 ...
 This method is very simple to implement, but it results in code hard to
 write, read and understand, and limits the enchancement capabilities
 (adding new events could be a problem).

 Other ideas?

I've often though about ways to merge event and exception handling. There are two fundamental types of each: 1) Ones you expect, and ones you don't, and 2) Ones you want to handle locally, and ones you want to handle globally. The "local" context is generally where "expected" events and exceptions are handled, and if there is no local handler, we "fall through" to a global context, which then falls through to the language or OS for default handling of events and exceptions we haven't explicitly handled at either the local or global levels. We also want to locally override the global handling when and where it suits our needs, and be able to do this both for exceptions and events. I believe it would be useful to have both contexts available for each. Fundamentally, I'd like to have the ability to make "events" look like traditional exceptions, and I'd also like to be able to make "exceptions" look like traditional events. Many high-level ORBs merge the two into the event system, but at the language level it may be useful to also be able to use something like the exception system for some events. There are also cases where events may need to be handled partially in the "event" domain, and the remainder handled in the "exception" domain. That is, do some of the handling as an event, then throw an exception for the rest. The inverse may be true for some exceptions (handle the exception partially, then generate an event), but I have no specific examples in mind. Things like local repair followed by global cleanup or state restoration/resetting come to mind. In real-time and embedded systems, most operational conditions are handled by the main path through the application code. Obvious failures are handled as such. But conditions at the edges of the performance envelope often demand processing outside the normal flow, and using events and exceptions are useful ways to gain access to this code without cluttering up the main execution path. In mose languages you have to choose one or the other. I'd like to have both. For D, would it be useful or possible to extend the exception system to handle general event handling? I haven't thought this all the way through yet, so I have no specific architecture, design or implementation recommendations. It's just my $0.02 at this point... -BobC
Feb 23 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
There are many ways to improve D in this area. However, since
Walter is busy with other things =| I guess a solution has
to be found which uses whatever is already there (in D).
Feb 23 2002
parent reply Ruslanas Abdrachimovas <anubis 03bar.ktu.lt> writes:
Pavel Minayev wrote:
 There are many ways to improve D in this area. However, since
 Walter is busy with other things =| I guess a solution has
 to be found which uses whatever is already there (in D).
 
 
 

And how about... JAVA style of event handling?
Mar 04 2002
next sibling parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Ruslanas Abdrachimovas" <anubis 03bar.ktu.lt> wrote in message
news:3C8388E2.8050703 03bar.ktu.lt...

 And how about... JAVA style of event handling?

... and it is?...
Mar 04 2002
parent reply Ruslanas Abdrachimovas <anubis 03bar.ktu.lt> writes:
Pavel Minayev wrote:
 "Ruslanas Abdrachimovas" <anubis 03bar.ktu.lt> wrote in message
 news:3C8388E2.8050703 03bar.ktu.lt...
 
 
And how about... JAVA style of event handling?

... and it is?...

Event dispatcher queue in separate thread... And who wants to listen to say JButton instance just does button.addActionListener(<ref to object implementing action listener interface>); .... More abstract: observer pattern with some additions (queue in separate thread) =] .... Ruslanas
Mar 04 2002
next sibling parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Ruslanas Abdrachimovas" <anubis 03bar.ktu.lt> wrote in message
news:3C839AA7.1090403 03bar.ktu.lt...

 Event dispatcher queue in separate thread... And who wants to listen to
 say JButton instance just does
 button.addActionListener(<ref to object implementing action listener
 interface>);

Doesn't this require creating a separate listener class for each control?
Mar 04 2002
parent "Richard Krehbiel" <rich kastle.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:a606fb$1p8f$1 digitaldaemon.com...
 "Ruslanas Abdrachimovas" <anubis 03bar.ktu.lt> wrote in message
 news:3C839AA7.1090403 03bar.ktu.lt...

 Event dispatcher queue in separate thread... And who wants to listen to
 say JButton instance just does
 button.addActionListener(<ref to object implementing action listener
 interface>);

Doesn't this require creating a separate listener class for each control?

Though I don't think it's specifically necessary, that's what JBuilder makes for you, an anonymous inner class (not just class instance, but a whole distinct class) for each event. -- Richard Krehbiel, Arlington, VA, USA rich kastle.com (work) or krehbiel3 comcast.net (personal)
Mar 04 2002
prev sibling parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Ruslanas Abdrachimovas" <anubis 03bar.ktu.lt> wrote in message
news:3C839AA7.1090403 03bar.ktu.lt...

 Event dispatcher queue in separate thread... And who wants to listen to
 say JButton instance just does
 button.addActionListener(<ref to object implementing action listener
 interface>);

Now I've read the specification, I understand... I'd thought of something like this already, but it requires tons of classes and objects to be created to handle all events in a more or less complex window... also listener objects will have to store pointer to their owner, and won't get access to privates - bad! Java provides a workaround in the form of anonymous inner classes, unfortunately there isn't anything like that in D. I've been thinking of another, not very safe, but (IMO) better alternative. Here's a sample snippet: /* library code */ interface Handler { } // just a dummy, to provide some typechecking private interface _Handler: Handler { void handle(Event); } class Button { Handler onClick; ... void clicked() { (cast(_Handler) onClick).handle(new ClickEvent); } } /* user code */ interface cmdOk_Click: Handler { void cmdOk_Click(Event); } interface cmdCancel_Click: Handler { void cmdCancel_Click(Event); } class MyForm: Form, cmdOk_Click, cmdCancel_Click { Button cmdOk, cmdCancel; this() { ... cmdOk.onClick = cast(cmdOk_Click) this; cmdCancel.onClick = cast(cmdCancel_Click) this; } void cmdOk_Click(Event e) { ... } void cmdCancel_Click(Event e) { ... } }
Mar 04 2002
parent reply "Richard Krehbiel" <rich kastle.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:a607ha$1psa$1 digitaldaemon.com...
 "Ruslanas Abdrachimovas" <anubis 03bar.ktu.lt> wrote in message
 news:3C839AA7.1090403 03bar.ktu.lt...

 Event dispatcher queue in separate thread... And who wants to listen to
 say JButton instance just does
 button.addActionListener(<ref to object implementing action listener
 interface>);

Now I've read the specification, I understand... I'd thought of something like this already, but it requires tons of classes and objects to be created to handle all events in a more or less complex window... also listener objects will have to store pointer to their owner, and won't get access to privates - bad! Java provides a workaround in the form of anonymous inner classes, unfortunately there isn't anything like that in D. I've been thinking of another, not very safe, but (IMO) better alternative. Here's a sample snippet: /* library code */ interface Handler { } // just a dummy, to provide some typechecking private interface _Handler: Handler { void handle(Event); } class Button { Handler onClick; ... void clicked() { (cast(_Handler) onClick).handle(new ClickEvent); } } /* user code */ interface cmdOk_Click: Handler { void cmdOk_Click(Event); } interface cmdCancel_Click: Handler { void cmdCancel_Click(Event); } class MyForm: Form, cmdOk_Click, cmdCancel_Click { Button cmdOk, cmdCancel; this() { ... cmdOk.onClick = cast(cmdOk_Click) this; cmdCancel.onClick = cast(cmdCancel_Click) this; } void cmdOk_Click(Event e) { ... } void cmdCancel_Click(Event e) { ... } }

Forgive me for being dense and/or naive, but I still don't see the difficulty with dispensing with intermediate event-receptor classes, but rather subclass all your GUI objects from supplied classes with predefined event-methods, and then implementing those methods you want. And if you think the difficulty is "too many potential callback methods leading to overly large vtbls" then see my other post regarding this issue: news://news.digitalmars.com/a6054t$1o03$1 digitaldaemon.com -- Richard Krehbiel, Arlington, VA, USA rich kastle.com (work) or krehbiel3 comcast.net (personal)
Mar 04 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Richard Krehbiel" <rich kastle.com> wrote in message
news:a609d8$1rd6$1 digitaldaemon.com...

 Forgive me for being dense and/or naive, but I still don't see the
 difficulty with dispensing with intermediate event-receptor classes, but
 rather subclass all your GUI objects from supplied classes with predefined
 event-methods, and then implementing those methods you want.

No MI, so you can't subclass from many classes, only from one. So you have to use interfaces. Now consider the following one: interface IButtonEvents { void onButtonClick(Event); } And a form: class MyForm: Form, IButtonEvents { Button cmdOk, cmdCancel; onButtonClick(Event e) { } } Now onButtonClick() will receive click messages from BOTH cmdOk and cmdCancel, and you have to use switch to provide separate implementation for each case - and what if there are 20 buttons on the form?.. this isn't far from your typical WindowProc style. What I want is the ability to bind a separate function for each event of each distinct _object_ (and not class). So if you have 10 buttons on your form, you should be able to have 10 onClick handlers, one for each button. I don't see how it can be done this way.
 And if you think the difficulty is "too many potential callback methods
 leading to overly large vtbls" then see my other post regarding this

Not a problem, IMO. If you have 30-40 event handlers, it's just 120-160 additional bytes in the vtable per class - not something worthy even remembering about.
Mar 04 2002
parent reply "Richard Krehbiel" <rich kastle.com> writes:
Hmmm....  Okay, I think I'm seeing the issue more correctly.

In VB, event handlers are located symbolically.  If a control's name is
"Button1" and it's trying to deliver a Click event, it looks for a method
"Button1_Click" in the form.  This is mighty convenient, but also very
symbolic.  Hmmm.

I'm finding myself resorting to the Java style, with event handler classes,
but not funky anonymous inner classes.  And I'm not really liking it.

If only D had *macros* this could be done rather conveniently.  (I don't
know why no one else can see the need for macros.  Just you wait until I've
written up the spec for the macro language; you'll see.)

--
Richard Krehbiel, Arlington, VA, USA
rich kastle.com (work) or krehbiel3 comcast.net  (personal)
Mar 05 2002
parent "Pavel Minayev" <evilone omen.ru> writes:
"Richard Krehbiel" <rich kastle.com> wrote in message
news:a62fdv$2ra5$1 digitaldaemon.com...

 In VB, event handlers are located symbolically.  If a control's name is
 "Button1" and it's trying to deliver a Click event, it looks for a method
 "Button1_Click" in the form.  This is mighty convenient, but also very
 symbolic.  Hmmm.

 I'm finding myself resorting to the Java style, with event handler

 but not funky anonymous inner classes.  And I'm not really liking it.

I'd prefer pointers to methods of objects (delegates in C# terminology), as seen in Delphi/VCL and C#. They solve all the problems, IMO: class Button { delegate void (*onClick)(Event); ... void clicked() { onClick(new Event); } } class MyForm: Form { Button cmdOk, cmdCancel; ... this() { cmdOk.onClick = &this.cmdOk_Click; cmdCancel.onClick = &this.cmdCancel_Click; } void cmdOk_Click(Event e) { ... } void cmdCancel_Click(Event e) { ... } } Unfortunately, delegates are very unlikely to be seen in D 1.0, from what I've heard...
Mar 05 2002
prev sibling parent "Richard Krehbiel" <rich kastle.com> writes:
"Ruslanas Abdrachimovas" <anubis 03bar.ktu.lt> wrote in message
news:3C8388E2.8050703 03bar.ktu.lt...
 Pavel Minayev wrote:
 There are many ways to improve D in this area. However, since
 Walter is busy with other things =| I guess a solution has
 to be found which uses whatever is already there (in D).

And how about... JAVA style of event handling?

Java SWING event handling is irritating in it's need/desire for weirdo anonymous intermediate classes. C# builds event handling in, but (also IMHO) the distinction between an event and a method is too subtle. I personally have always thought that event handling should be a simple matter of virtual methods used as callbacks. The drawback is the need for large vtables for lots of potential callbacks, most of which aren't used. But I think that, in these days of cheap and plentiful memory, the simplicity of the approach outweighs the memory savings. Perhaps if you *really* want to talk about saving vtable space, then consider building "sparse vtable" support into the language; have the compiler/linker automatically (or by hint) realize that overrides from a particular class are rare, and instead of a plain vtable, make a "sparse" vtable that's sorted by method ID (which would be equal to the offset in the regular vtable format), and where method dispatches do a binary search for the method, and if not found, automatically reroute to the superclass. But in the end, I don't see a great need for any special "event handling" facility. -- Richard Krehbiel, Arlington, VA, USA rich kastle.com (work) or krehbiel3 comcast.net (personal)
Mar 04 2002
prev sibling parent Roland <rv ronetech.com> writes:
Pavel Minayev a écrit :

 This is something I would really like to hear others' opinions
 about: event handling in my GUI library, WinD. Currently, it
 tries to mimic the Borland's approach (pointers to methods),
 and does it by some rather dirty hacks, like modifying the
 vtable at run-time, besides, it's highly error-prone... Needless
 to say, I'm not very satisfied with this solution. Should it
 be dropped in favor of other method, and if so, which? I only
 have one idea: a single handler for ALL events from this and
 child controls:

     class MyForm: Form
     {
         TextBox txtName;
         Button cmdOk;

         ...

         void eventHandler(Event e)
         {
             switch (e.sender)
             {
             case txtName:
                 switch (e.type)
                 {
                 case Event.Click:
                     ...
                 case Event.Change:
                     ...
                 }
                 break;
             case cmdOk:
                 if (e.type == Event.Click)
                     ...
             }
         }
     }

 This method is very simple to implement, but it results in code hard to
 write, read and understand, and limits the enchancement capabilities
 (adding new events could be a problem).

 Other ideas?

Warning: This post is very theorical and not pactical at all. I'm not very aware of how D is now. I don't use it yet. I hope this post will not lower my already poor signal/noise ratio. Lets go: A system that generate events, is like an electronic chip, witch pins change of state to signal something. It you want your own chip to react to the event, there is no other way than to connect an input pin of your chip to the proper output signal. In HDL (Hardware Description Langage), to connect an input pin to an output pin, you use the '=' operator. In my opinion, it would be nice if interfaces and signal handling in D takes inspiration from HDL langages. (may be it is already the case for interfaces) You defines 'chips' (classes) with input and output and you connect them programmaticaly, at compile time or at run time (multiplexers). Note that an output pin can be connected to many input pins, and all input pins recieve the information at the same time. A compiler should emulate that. (carefull with race conditions ! the compiler should detect them) Roland
Mar 04 2002