3
votes

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?

2
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
@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
@Mason After reading your answer, yes I can see how that would be an issue.Jerry Dodge
Why are you accessing out of bounds? Your code is broken? Best fix it.David Heffernan
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

2 Answers

5
votes

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.

2
votes

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.