What is good practice for error handling in math-related functions? I'm building up a library (module) of specialized functions and my main purpose is to make debugging easier for the code calling these functions -- not to make a shiny user-friendly error handling facility.
Below is a simple example in VBA, but I'm interested in hearing from other languages as well. I'm not quite sure where I should be returning an error message/status/flag. As an extra argument?
Function AddArrays(arr1, arr2)
Dim i As Long
Dim result As Variant
' Some error trapping code here, e.g.
' - Are input arrays of same size?
' - Are input arrays numeric? (can't add strings, objects...)
' - Etc.
' If no errors found, do the actual work...
ReDim result(LBound(arr1) To UBound(arr1))
For i = LBound(arr1) To UBound(arr1)
result(i) = arr1(i) + arr2(i)
Next i
AddArrays = result
End Function
or something like the following. The function returns a boolean "success" flag (as in the example below, which would return False if the input arrays weren't numeric etc.), or an error number/message of some other type.
Function AddArrays(arr1, arr2, result) As Boolean
' same code as above
AddArrays = booSuccess
End Function
However I'm not too crazy about this, since it ruins the nice and readable calling syntax, i.e. can't say c = AddArrays(a,b)
anymore.
I'm open to suggestions!
There's lots of ways to trap errors, some better than others. Alot of it depends on on the nature of the error and how you want to handle it.
1st: In your examples, you aren't handling the basic compiling & runtime errors (see code below).
2nd: Using the framework example above, you can add all the if-then logical error trapping statements you want & feed it to the EH step. You can even add multiple EH steps if your function is complex enough. Setting things up this way allows you to find the particular function where your logic error occurred.
3rd: In your last example, ending that function as a boolean is not the best method. If you were able to add the 2 arrays, then that function should return the resultant array. If not, it should throw up a msgbox-style error.
4th: I recently started doing a little trick that can be very helpful in some situations. In your VBA Editor, go to Tools->Options->General->Break on ALL errors. This is very helpful when you already have your error handling code in place, but you want to go the exact line where the error occurred and you don't feel like deleting perfectly good code.
Example: Let's say you want to catch an error that wouldn't be caught normally by VBA, i.e. an integer variable should always have a value >2. Somewhere in your code, say
If intvar<=2 then goto EH
. Then in your EH step, addIf intvar<=2 then msgbox "Intvar=" & Intvar
.Obviously error handling in general is a big topic, and what the best practice is depends a lot on the capabilities of the language you're working with and how the routine you're coding fits in with other routines. So I'll constrain my answer to VBA (used within Excel) and library-type routines of the sort you're describing.
Exceptions vs. Error Codes in Library Routines
In this case, I would not use a return code. VBA supports a form of exception handling that, while not as powerful as the more standard form found in C++/Java/??.NET, is pretty similar. So the advice from those languages generally applies. You use exceptions to tell calling routines that the called routine can't do it's job for whatever reason. You handle exceptions at the lowest level where you can do something meaningful about that failue.
Bjarne Stroustrup gives a very good explanation of why exceptions are better than error codes for this kind of situation in this book. (The book is about C++, but the principles behind C++ exception handling and VBA error handling are the same.)
http://www2.research.att.com/~bs/3rd.html
Here is a nice excerpt from Section 8.3:
Sections 14.1 and 14.9 also address exceptions vs. error codes in a library context. (There is a copy of the book online at archive.org.)
There is probably lots more about this on stackoverflow. I just found this, for example:
Exception vs. error-code vs. assert
(There can be pitfalls involving proper management of resources that must be cleaned up when using exceptions, but they don't really apply here.)
Exceptions in VBA
Here is how raising an exception looks in VBA (although the VBA terminology is "raising an error"):
If this routine doesn't catch the error, VBA will give other routines above it in the call stack a chance to (See this: VBA Error "Bubble Up"). Here is how a caller might do so:
What "do something meaningful" means depends on the context of your application. In the case of my caller example above, it decides that some errors should be handled by returning an error value that Excel can put in a worksheet cell, while others require a nasty alert. (Here's where the case of VBA within Excel is actually not a bad specific example, because lots of applications make a distinction between internal and external routines, and between exceptions you expect to be able to handle and error conditions that you just want to know about but for which you have no response.)
Don't Forget Assertions
Because you mentioned debugging, it's also worth noting the role of assertions. If you expect AddArrays to only ever be called by routines that have actually created their own arrays or otherwise verified they are using arrays, you might do this:
A fantastic discussion of the difference between assertions and exceptions is here:
Debug.Assert vs Exception Throwing
I gave an example here:
Is assert evil?
Some VBA Advice About General Array Handling Routines
Finally, as a VBA-specific note, there are VBA variants and arrays come with a number of pitfalls that must be avoided when you're trying to write general library routines. Arrays might have more than one dimension, their elements might be objects or other arrays, their start and end indices might be anything, etc. Here is an example (untested and not trying to be exhaustive) that accounts for some of that:
First, PowerUser gave you a good answer already--this is an expansion on that one.
A trick that I just learned is the "double resume", thus:
What happens here is that in the execution of finished code, your code throws up a MsgBox when an error is encountered, then runs the
Exit Function
statement & goes on its way (just the same as dropping out the bottom withEnd Function
). However, when you're debugging and you get that MsgBox you instead do a manual Ctrl-Break, then set next statement (Ctrl-F9) to the unadornedResume
and press F8 to step--it goes right back to the line that threw the error. You don't even have to take the extraResume
statements out, since they will never execute without manual intervention.The other point on which I want to argue (gently) with PowerUser is in the final example. I think it's best to avoid unneeded
GoTo
statements. A better approach isIf intvar<=2 then err.raise SomeCustomNumber
. Make sure you use a number that isn't already in use--search 'VB custom error' for more information.