3

I have some delphi code, kind of like this:

try
  //some code
  //occasionally throws an exception here, for example an EIndexOutOfRangeException
  //more code...should get skipped if exception is thrown
finally
  // there may or may not be any important cleanup code here
end;

The exception in this case is not something that needs handling beyond breaking out of the try block. So, before mad-except was added to the project for error troubleshooting, this code was "working". But now I'm getting bug reports because MadExcept is reporting the uncaught exception.

Related question, MadExcept triggers on try finally indicates the behavior of MadExcept breaking in such circumstance is "expected" because the exception isn't "handled".

I would like some clarification about what my options are as far ways to prevent a mad-except dialog from popping up when this code runs, regardless of whether there's an internal exception being thrown and ignored.

So I'm correct in thinking there's no switch to disable MadExcept from breaking on unhanded exceptions in a try/finally block? And I'm going to need to explictly "catch" the exception even if I wish to ignore it?

Should I be doing something like this (ignore any exception):

try
  //some code
  //sometimes throws EIndexOutOfRangeException here
  //more code...should get skipped if exception is thrown
except do begin end;
end;

or perhaps (ignore a very specific exception):

try
  //some code
  //sometimes throws EIndexOutOfRangeException here
  //more code...should get skipped if exception is thrown
except on E : EIndexOutOfRangeException do begin end;
end;

or maybe it needs to be:

try
  try
    //some code
    //sometimes throws EIndexOutOfRangeException here
    //more code...should get skipped if exception is thrown
  except on E : EIndexOutOfRangeException do begin end;
finally
  // some cleanup code
end;

If all three of those are valid solutions, should I prefer one over the other for any reason?

Community
  • 1
  • 1
Jessica Brown
  • 8,222
  • 7
  • 46
  • 82
  • I don't see any reason not to use any of them. It really depends on the code which is being executed. If you need to clean up some stuff, the last one is your option. If not, then ask yourself if you need to handle specific types of exceptions differently. You're just ignoring it in either case anyway, so the first one is all you need. – Jerry Dodge Jul 08 '14 at 23:51
  • @Jerry: The first one is not needed, and in fact is actively harmful because it can mask unanticipated exceptions. The second example is the correct one. – Mason Wheeler Jul 08 '14 at 23:59
  • @Mason After reading your answer, yes I can see how that would be an issue. – Jerry Dodge Jul 09 '14 at 00:02
  • 1
    Why are you accessing out of bounds? Your code is broken? Best fix it. – David Heffernan Jul 09 '14 at 00:47
  • Yes, in the general case it would be better do some defensive programming and do explicit bounds checking beforehand, but this is in some performance critical code in a tight loop and where it turns out being out of bounds/invalid is pretty rare. It ends up being faster to skip the bounds check for the average case and deal with the expensiveness of exception creation in the uncommon case. – Jessica Brown Jul 09 '14 at 03:56
  • That sounds a little odd to me. But even so, taking you at face value, if what you say is true, then your code in the question is broken irrespective of madExcept. This question has nothing at all to do with madExcept and is all about your understanding of how to handle (or not) exceptions. – David Heffernan Jul 09 '14 at 06:08
  • I question your assertion that you are skipping bounds checking for performance. How do you think that the exception is raised? The code performs bounds checking everytime. And raising exceptions and then catching them is slower than the non-exception based alternative. So if you care about performance, then for sure you are optimising the wrong way. You need a collection that doesn't bounds check, and you add explicit bounds checking. – David Heffernan Jul 09 '14 at 06:56

2 Answers2

5

So I'm correct in thinking there's no switch to disable MadExcept from breaking on unhanded exceptions in a try/finally block?

Yes. try/finally is not exception handling; it's guaranteed cleanup, whether or not an exception occurs. As such, try/finally blocks are completely irrelevant to exception handling tools such as MadExcept.

And I'm going to need to explicitly "catch" the exception even if I wish to ignore it?

Yes. That's how exceptions work. They work their way down the stack until they find a handler that catches them. If no such handler exists, the OS interprets it as a crash and terminates the program. Delphi's TApplication object installs a handler very close to the bottom of the call stack so that your program won't crash, and MadExcept hooks this so that if an exception reaches this point, a report will be generated.

If you want to ignore an exception, yes, you do need to catch it, because what you are doing is formally "handling the exception by catching it at this point in the stack unwinding and ignoring it." That part about "catching it at this point in the stack unwinding" is important, since it means that the stack unwinding halts at that point and the program resumes normal execution. If you just ignored it, (ie. did nothing about it in your code, including not installing an exception handler,) it would continue to unwind the stack all the way to the default handler, whether or not you had MadExcept installed.

So yeah, in this case, example #2 is the correct course of action. Example #3 would be valid too, if you have cleanup code that needs to be performed at this point. But example #1 should never be done under any circumstances because it means you might end up ignoring an exception that you were not anticipating, and then you end up with corruption in your program and you never become aware of it.

Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477
  • I would opt for example 3 but swap the `try/finally` and `try/except` around. Put the `try/finally` inside of the `try/except` instead of the other way around. But either way, yes, have the `except` catch whatever exceptions can be handled+discarded gracefully at that moment so MadExcept will not see them. – Remy Lebeau Jul 09 '14 at 00:05
  • 2
    @Remy The key is to get the execution flow right. madExcept is irrelevant. Handling the exception, or not, changes the behaviour of the program. Changes what code is executed. One must get that right. If that means madExcept sees an unhandled exception that should not create a bug report, that needs to be handled in the ME unhandled exception handler. Handling the exception at a low level is the wrong way to change the way ME treats it. – David Heffernan Jul 09 '14 at 01:48
2

It seems to me that you have a fundamental mis-understanding about what finally means.

A finally block does not influence the propagation of an exception. It just ensures that the finally block will execute, even if an exception has been raised, or the normal execution flow has been modified by exit, break etc.

Take the try/finally out and madExcept will still report that the program raised an exception that was not handled.

There are ways to tell madExcept to ignore certain exceptions. For instance some exceptions should be silent. The canonical example of such is EAbort. You would use RegisterExceptionHandler to customise how unhandled exceptions are treated. Although, as I will explain, I doubt this to be the solution for your problem.

What you need to do next is forget about madExcept. You need to work out what to do about this exception. Do you want to handle it here, or do you need to let the exception propagate? Only you can really know that. But madExcept is not the driver here. What must drive your decision is the correct execution of your program. Should the exception be handled or not, to make your program behave correctly? You must get that right first, and then worry about madExcept.

If you need to handle it here, then handle it selectively. Don't catch all exceptions. That's an absolute no-no. But if you handle it here, ask yourself if that is sensible. The code failed to perform some action. Is there some subsequent code that relies on that action succeeding? By handling the error, and ignoring it as you propose, you are forcing all subsequent code to be ambivalent about the success or failure of this action. That seems highly dubious to me.

Now, the exception is EIndexOutOfRangeException. That means you wrote something like A[i] where the value of i is invalid. I cannot think of a scenario where that would be acceptable. So it looks to me that your program contains an error and is simply using an invalid index. You should fix that error properly by not accessing out of bounds. Don't suppress the exception, stop it being raised.

Another way to look at this. How can you tell the difference between your current situation, and that which would arise from writing A[-i] instead of A[i]? Suppressing the exception means that you cannot detect such egregious errors as this.

The bottom line, so far as I can tell, is that madExcept is reporting an error in your code. You should regard madExcept as your friend and listen to what it says. It is telling you that you have a defect in your code that should be fixed.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • In case you didn't see my other comment above, in general I prefer to do explicit bounds checking beforehand to be defensive in my programming, but the example I happened to pick to describe, it is part of some performance critical code in a tight loop where array lookup failure is rare. It ends up being faster in the most common case to skip the bounds check for the average case and deal with the expensiveness of exception throwing in the uncommon case where the lookup fails. – Jessica Brown Jul 09 '14 at 05:05
  • 1
    I read the question rather than comments. In any case, I still think that the points in my answer stand. Your code was broken before. This is nothing at all to do with madExcept. If the intent of your code is for you to handle this error then you must do so. It's no good to allow it to float up. I'm disappointed that you don't seem to be understanding the point that I am making. I think you are missing an opportunity to learn something important. – David Heffernan Jul 09 '14 at 06:06
  • I think I see what you're getting at, that the exception will continue to propagate if there's a finally but no catch("except on"), and more than likely that may fundamentally *not* be what I want to have happen. In this case, it should have had a "except on EIndexOutOfRangeException" block and not a finally block. – Jessica Brown Jul 09 '14 at 16:48
  • The first point to stress is that madExcept is not relevant here. What counts is the control flow of your program, something that madExcept does not alter. A finally block is quite different from an except. The former always executes. Both with normal and exceptional conditions. The latter, executes only in exceptional conditions. – David Heffernan Jul 09 '14 at 17:04
  • 1
    Anyway I'm still pretty confident that you are doing something wrong. Handling `EIndexOutOfRangeException` exceptions and treating them as normal is an epic code smell. – David Heffernan Jul 09 '14 at 19:47
  • After looking at the code a bit I did decide the lookup table function should be catching the exception and returning "not found" from the function where the error is thrown, not passing the exception up the chain, so I'm not disagreeing with your assessment that there's a bug in the code unrelated to madexcept's presence. I think some upstream error checking was masking the effects of this part of the code being incorrect. – Jessica Brown Jul 09 '14 at 21:22
  • My instincts tell me that your code should avoid the exception being thrown in the first place. – David Heffernan Jul 09 '14 at 21:34