BBC BASIC for Windows
Programming >> BBC BASIC language >> Cascaded ON CLOSE
http://bb4w.conforums.com/index.cgi?board=language&action=display&num=1276919718

Cascaded ON CLOSE
Post by Michael Hutton on Jun 19th, 2010, 03:55am

Richard,

I was wondering what your thoughts would be to add cascaded ON CLOSE handling to BB4W?

It could be a useful feature for library writers who would like to cleanup any objects their library creates without having the user to clean them in the main program.

For instance:
Code:
      REM Cascaded ON CLOSE Handling?
      
      ON CLOSE PROCCleanup:END
      
      PROC_INIT_MYLIB
      
      REPEAT
        WAIT 1
      UNTIL FALSE
      
      
      END
      
      DEFPROCCleanup
      PRINT "Cleaning!"
      ENDPROC
      
      DEFPROC_INIT_MYLIB
      PRIVATE MYLIBVARS{}
      PROCPRIVATE(MYLIBVARS{})
      MYLIBVARS.close% = !392
      ON CLOSE PROC_MYLIB_Cleanup : END
      
      REM Create my objects here and add their handles to the MYLIBVARS.handles%()

      ENDPROC
      
      
      DEF PROC_MYLIB_Cleanup
      PRINT "Cleaning the Library"
      PRIVATE MYLIBVARS{}
      PROCPRIVATE(MYLIBVARS{})
      
      REM Cleanup the objects
      LOCAL I%
      FOR I% = 0 TO 10
      IF MYLIBVARS.handle%(I%) THEN SYS"DeleteObject", MYLIBVARS.handle%(I%)
      NEXT

      !392 = MYLIBVARS.close%
      WM_CLOSE = &10
      SYS "SendMessage", @hwnd%, WM_CLOSE, 0,0
      
      ENDPROC
      
      DEF PROCPRIVATE(RETURN t{})
      PRIVATE p{}
      DIM p{close%. handle%(10)}
      !(^t{}+0) = !(^p{}+0)
      !(^t{}+4) = !(^p{}+4)
      ENDPROC
 


Although this works, it is not guaranteed to work if an ON CLOSE statement is defined after initialising the library...

An ON CLOSE LOCAL is obviously not appropriate for the library.

I think it could be a useful feature so that any ON CLOSE statement (not ON CLOSE LOCAL) adds a pointer to a linked list of pointers for code to be processed when a WM_CLOSE message is received by a window.

It may be a feature which would help with library encapsulation?

Michael
Re: Cascaded ON CLOSE
Post by admin on Jun 19th, 2010, 10:11am

on Jun 19th, 2010, 03:55am, Michael Hutton wrote:
I was wondering what your thoughts would be to add cascaded ON CLOSE handling to BB4W?

Well, as you know, I am treating the BBC BASIC for Windows interpreter as effectively frozen. So the question to ask is not what changes might be made to BB4W, but what workarounds are available without needing to make any changes.

It seems to me that the approach you've adopted is the right way to proceed. However, rather than restoring the ON CLOSE vector and sending another WM_CLOSE message, as you do, I would have been inclined to jump directly to the original handler, as below. It's easier and quicker.

Quote:
Although this works, it is not guaranteed to work if an ON CLOSE statement is defined after initialising the library...

I don't see that as an issue; the library simply has to specify that its initialisation function must be called after any ON CLOSE. Any theoretical extension to BB4W would need to impose a similar constraint, because automatically cascading ON CLOSE wouldn't be compatible with existing programs (and imagine what might happen if the ON CLOSE was in the main loop, something I quite often do!). By default a later ON CLOSE would have to replace the existing vector as now.

Code:
      REM Cascaded ON CLOSE Handling?
      
      ON CLOSE PROCCleanup:END
      
      PROC_INIT_MYLIB
      
      REPEAT
        WAIT 1
      UNTIL FALSE
      
      END
      
      DEFPROCCleanup
      PRINT "Cleaning!"
      ENDPROC
      
      DEFPROC_INIT_MYLIB
      PRIVATE MYLIBVARS{}
      PROCPRIVATE(MYLIBVARS{})
      MYLIBVARS.close% = !392
      ON CLOSE PROC_MYLIB_Cleanup : END
      
      REM Create my objects here and add their handles to the MYLIBVARS.handles%()
      
      ENDPROC
      
      
      DEF PROC_MYLIB_Cleanup
      PRINT "Cleaning the Library"
      PRIVATE MYLIBVARS{}
      PROCPRIVATE(MYLIBVARS{})
      
      REM Cleanup the objects
      LOCAL I%
      FOR I% = 0 TO 10
        IF MYLIBVARS.handle%(I%) THEN SYS"DeleteObject", MYLIBVARS.handle%(I%)
      NEXT
      
      PROC(^MYLIBVARS.close%)
      ENDPROC
      
      DEF PROCPRIVATE(RETURN t{})
      PRIVATE p{}
      DIM p{close%,handle%(10)}
      !(^t{}+0) = !(^p{}+0)
      !(^t{}+4) = !(^p{}+4)
      ENDPROC 

Richard.
Re: Cascaded ON CLOSE
Post by Michael Hutton on Jun 20th, 2010, 08:09am

weird, I thought I had replied earlier on.... anyway:

I see your point about the problem if the ON CLOSE statement is in a loop.

I'll develop my thoughts a bit further, maybe a CLEANUPLIB could be fashioned to keep track of objects created by the user. For example, if someone creates an object

PROC_CLEANUPLIB_addObject( type )

and then the CLEANUPLIB could auto clean at any ON CLOSE event....

Michael
Re: Cascaded ON CLOSE
Post by admin on Jun 20th, 2010, 09:36am

on Jun 20th, 2010, 08:09am, Michael Hutton wrote:
weird, I thought I had replied earlier on

I've never known Conforums lose a post. Almost certainly what you did was click Preview but never click Post. You don't get any warning if you navigate away from the page without posting your message (the Wiki is better in that respect).

You didn't comment on my suggested modification to your code of jumping directly to the old ON CLOSE handler, rather than messily restoring the vector and sending another WM_CLOSE message. I think that's a very worthwhile improvement.

Richard.
Re: Cascaded ON CLOSE
Post by Michael Hutton on Jun 22nd, 2010, 12:33am

on Jun 20th, 2010, 09:36am, Richard Russell wrote:
Almost certainly what you did was click Preview but never click Post.

Yes, I was pretty sure that is what I did. Frustrating.

Quote:
You didn't comment on my suggested modification to your code of jumping directly to the old ON CLOSE handler, rather than messily restoring the vector and sending another WM_CLOSE message. I think that's a very worthwhile improvement.

Although sending the message is clumsier, it could be safer. For example, what happens if, the main ON CLOSE statement ends with a RETURN (unlikely, but possible):

Code:
ON CLOSE RETURN
 


jumping with the PROC(^MYLIBVARS.close%) will give a "not in a subroutine" error, whereas sending a message to the window will produce the expected behaviour.

Michael
Re: Cascaded ON CLOSE
Post by admin on Jun 22nd, 2010, 08:48am

on Jun 22nd, 2010, 12:33am, Michael Hutton wrote:
jumping with the PROC(^MYLIBVARS.close%) will give a "not in a subroutine" error, whereas sending a message to the window will produce the expected behaviour.

ON CLOSE RETURN is used to ignore the Close button (and Alt-F4) entirely, the idea being that the program will continue execution as if nothing happened.

In that case your suggestion won't produce "the expected behaviour". If the user presses Close or Alt-F4 it won't be ignored, instead your library will perform its cleanup but the program will keep running! The result is likely to be a failure when the program subsequently attempts to use one of your library functions.

So, in fact, my code is actually better than yours, precisely because it results in a 'Not in a subroutine' error! It alerts the user to the fact that he's used an ON CLOSE in a way that is not compatible with your 'cascaded' handling.

Nice try, but no cigar!

Richard.
Re: Cascaded ON CLOSE
Post by Michael Hutton on Jun 22nd, 2010, 10:08am

I did think of that, as you say, using the RETURN statement with an ON CLOSE would invoke the cleanup of the Library objects potentially creating an error when they are encountered again.

The upshot is to warn the user of the Library not to use a RETURN with the ON CLOSE statement when using the library.

However, I don't see another explicit advantage of jumping straight to the ON CLOSE vectors code rather than sending a WM_CLOSE message to the window, except maybe the length of code. Isn't it six of one and half a dozen of the other?

Michael
Re: Cascaded ON CLOSE
Post by admin on Jun 22nd, 2010, 11:40am

on Jun 22nd, 2010, 10:08am, Michael Hutton wrote:
However, I don't see another explicit advantage of jumping straight to the ON CLOSE vectors code rather than sending a WM_CLOSE message to the window, except maybe the length of code. Isn't it six of one and half a dozen of the other?

Well, I gave you one good reason why jumping to the vector is better: it will result in a 'Not in a subroutine' error if the user has an ON CLOSE RETURN. Issuing an error must be better than relying on the user noticing a warning in the library documentation!

Another thing is that your code has a potential weakness. After you send the WM_CLOSE message, you immediately exit from PROC_MYLIB_Cleanup, and then you execute an END statement:

Code:
      ON CLOSE PROC_MYLIB_Cleanup : END 

END disables ON CLOSE altogether, so it's possible the user's handler may never be called, if the END gets executed before the WM_CLOSE message has worked its way through the system.

Admittedly this is an unlikely scenario, because the chances are that the user's ON CLOSE will have been activated (as a result of your WM_CLOSE message) before the interpreter reaches the END statement - but this could easily change if I modified BB4W. For example, one thing I've considered more than once is testing for an 'interrupt' not every statement but (say) every five statements, to reduce the overhead. If I made that change your code would be guaranteed to fail.

Of course you could overcome this problem by following the SYS "SendMessage" with an 'infinite loop', rather than exiting via the ENDPROC to the END statement:

Code:
      SYS "SendMessage", @hwnd%, WM_CLOSE, 0,0
      REPEAT : WAIT 0 : UNTIL FALSE 

But why use a complicated solution when a simpler, faster, more straightforward one is available?

Richard.

Re: Cascaded ON CLOSE
Post by admin on Sep 24th, 2010, 4:32pm

It's taken a while, but I've finally worked out how to do this elegantly. The technique is described in this Wiki article:

http://bb4w.wikispaces.com/Cascaded+ON+CLOSE+handling

Here's the code of a demo program:

Code:
      PRINT "Click Close  to test cascaded ON CLOSE handling"
      PRINT "Press Escape to test cascaded ON ERROR handling"
      PRINT
 
      ON ERROR PRINT "Error handled in Main: " REPORT$ : END
      ON CLOSE PRINT "Close handled in Main" : END
      PROC0
      END
 
      DEF PROC0
      LOCAL dummy
      ON CLOSE LOCAL ERROR 111, "Close"
      ON ERROR LOCAL RESTORE LOCAL \
      \ IF ERR = 111 PRINT "Close handled in PROC0" : PROCclose ELSE \
      \ PRINT "Error handled in PROC0" : ERROR ERR,REPORT$
      dummy = FN1
      ENDPROC
 
      DEF FN1
      ON CLOSE LOCAL ERROR 111, "Close"
      ON ERROR LOCAL RESTORE LOCAL \
      \ IF ERR = 111 PRINT "Close handled in FN1" : PROCclose ELSE \
      \ PRINT "Error handled in FN1" : ERROR ERR,REPORT$
      PROC2
      = 1
 
      DEF PROC2
      ON CLOSE LOCAL ERROR 111, "Close"
      ON ERROR LOCAL RESTORE LOCAL \
      \ IF ERR = 111 PRINT "Close handled in PROC2" : PROCclose ELSE \
      \ PRINT "Error handled in PROC2" : ERROR ERR,REPORT$
      REPEAT
        WAIT 1
      UNTIL FALSE
      ENDPROC
 
      DEF PROCclose
      IF !392 PROC(^@%+392) ELSE QUIT
      ENDPROC 

Richard.
Re: Cascaded ON CLOSE
Post by Michael Hutton on Oct 8th, 2010, 12:37pm

I think I understood the code but surely this is still a ON CLOSE LOCAL solution. I am not sure I can see how to incorporate this code into a library which would take care of it's own clean up of objects it creates unless you ask the programmer to put their main program in a PROC (in this case PROC2) which the library calls. Hence if an ON CLOSE is detected in PROC2 the cascading clean up is performed.

As soon as you exit the PROC2 all the ON CLOSE LOCALs are 'forgotten'. Or yet again have I missed the point.

Michael


Re: Cascaded ON CLOSE
Post by admin on Oct 8th, 2010, 3:17pm

on Oct 8th, 2010, 12:37pm, Michael Hutton wrote:
I am not sure I can see how to incorporate this code into a library which would take care of it's own clean up of objects it creates

Sorry, I'm being a bit slow today (it doesn't help having a nasty cough for the last 7 weeks that won't go away). You'll have to explain what you mean in more detail.

As an experiment I've transferred PROC2 into a library, to simulate the situation you describe. There's nothing 'shared' between the library and the main prog as far as I can see (I've chosen a different error code and called PROCclose something different) and the main prog doesn't have to know that any 'local' CLOSE handling happens in the library. But it all seems to work as it did before.

Quote:
As soon as you exit the PROC2 all the ON CLOSE LOCALs are 'forgotten'.

In what sense "forgotten"? If I move the WAIT code out of PROC2 and instead put it in FN1 after the PROC2 statement, a CLOSE still seems to be trapped at each 'level'.

Richard.

Don't put an apostrophe in ITS unless you mean IT IS: http://www.youtube.com/watch?v=Vc2aSz9Ficw

Re: Cascaded ON CLOSE
Post by admin on Oct 8th, 2010, 4:58pm

on Oct 8th, 2010, 12:37pm, Michael Hutton wrote:
I think I understood the code but surely this is still a ON CLOSE LOCAL solution.

The title of this thread is Cascaded ON CLOSE so of course it's an ON CLOSE LOCAL solution, because that's what you need in that case! In the same way, the solution to the Cascaded ON ERROR problem is an ON ERROR LOCAL solution.

If you're asking about a situation where a library creates a global object, which then needs to be cleared up when the program is eventually closed, that's got nothing to do with cascaded ON CLOSE or ON ERROR handling, and is off-topic for this thread.

The solution to that issue is for the library to provide its own cleanup procedure that the user must call from the main program in his global cleanup code, typically activated from both his ON ERROR and ON CLOSE handlers. Many libraries have just that:

etc.

Richard.