Re: Try/Catch
- Posted by petelomax Jan 01, 2015
- 6345 views
I would think that you need some kind of stack unwind.
I think you misunderstand. I meant to state the very obvious - RTFatal currently stops code execution, and that behavior would have to be altered in order to handle exceptions.
I'd suggested that opcodes be put in place to check the return status from a routine that might set a fatal condition.
Well, most things can run out of memory and all variables can be unassigned, so that's at least 80% of all opcodes. That strategy will make programs nearly twice as big and half as fast.
Anyway, when you get what I mean by stack unwind, I am sure you will agree it is quite essential.
You can't simply perform a call stack unwind because traditional try/catch blocks can potentially be nested.
Plus, you need to handle the exception at the local level, where the exception took place. How is unwinding to a prior call helpful, if a local resource needs to taken care of? The prior call can't see into the routine with an exception.
You have to unwind the stack to the context where the exception handling code can execute:
function fdiv(atom a, atom b) return a/b end function function g(atom c, atom d) return fdiv(c,d) end function function h(atom e, atom f) return fdiv(e,f) end function procedure p() try ? g(i,j) catch puts(1,"error in g(i,j)") end try end procedure procedure q() try ? h(k,l) catch puts(1,"error in h(k,l)") end try end procedure
When you execute/resume at the exception handling code (puts) in p()/q(), the context/call stack must be unwound to that of p/q, not left as it was in fdiv. There may well be a saved context that the exception handler can examine, but the actively running context/stack frame as the exception handling code executes cannot be one from a completely different routine. In Eu, I doubt there is any way round the fact that by the time the puts is executed, a,b,c,d,e, and f may all have been deallocated.
All excellent arguments for not adding finally (an awful, stupid, confusing concept) to Euphoria.
Please explain your issue with finally.
I do not like anything which looks like it is doing something else. If I see return x, or throw(blah), then I expect it to do just that, not nip down and execute some other block of code first, or worse, have a return 1 quietly overridden by a return 2 in the finally block.
For handling system resources, it's essential that they be released correctly. finally guarantees that this will have the opportunity to happen.
How is this bad thing?
I don't understand. Is proper, obvious, clear and straightforward code not going to be executed?
What "guarantee" are you offering that is not already there?
Are you intending to have filtering such that handlers only deal with certain exceptions and implicitly re-throw any not explicitly dealt with?
If the main point of exception handling is to separate error handling code from the main logic, what is wrong with putting such things in a separate routine?
The point of structured exception handling is to handle exceptions in a structured manner.
One huge problem with calling cleanup() is that it hasn't got access to the local variables.
How do you close a resource in cleanup() when you don't have the handle to it?
cleanup(handle), or as below
How do you save the user's current work when #cleanup() can't see the data structure the user was working on?
cleanup(data_structure_the_user_was_working_on), or as below
How can you handle the exception at a level the exception didn't happen on?
I have no idea what that means. You handle exceptions in the exception handling code, after the call stack has been appropriately unwound to the correct/calling level.
In the above example, the puts() occurs in p() or q(), and not in fdiv() where the error actually happened.
It makes me madder than hell to think of such a horrible backward construct being added to Eu, if two simple calls would do.
That's a big "if".
Often, to simple calls won't do.
How you you pass the context of the current call to your cleanup() routine?
cleanup(context), or as below
Having try/catch scoped to a block inside the offending call solves that problem.
Of course if you are not re-throwing an exception, which I would have thought is more likely anyway, or are prepared to use a simple flag/return value, <such code> can and should just go after the end try.
I'm not sure I follow you here. Whether you re-throw an exception depends entirely on what the exception was, and how it should be handled.
If you are prepared to use the odd control flag and/or result variable, you can write all your cleanup code locally, and you don't need a cleanup() with umpteen parameters.
Of course the other burning question is why someone might want exception handling
Because exceptions need to be handled.
Halting isn't a robust way of handling exceptions.
- to write good quality robust code, but without the messiness of error code handling
You've lost me here. try/catch is error handling. Euphoria currently hasn't got solid error handling.
What "error code handling" are you referring to?
try/catch is exception handling. Euphoria has always had perfectly good error code handling, by which I mean open() returns -1, get() returns GET_FAIL, or that you can test for divide by zero first.
Hopefully some parts of what I am saying are helpful, and not too antagonistic.
Pete