TITLE: Propogating errors with and without exceptions KH = Kevin J Hopps (kjhopps@mmm.com) JK = kanze@lts.sel.alcatel.de (James Kanze), 23 Nov 95 KH: |> The expression mentioned below is |> vec[i++] = vec[j++] JK: |> > Take your expression, for example. And suppose that operator[] is |> > bounds checking (and thus, can generate an error condition). I imagine |> > that in most cases, a bounds check error is indicative of a programming |> > error, and should result in the program aborting. However, a totally |> > robust and general array class will let the user handle this, by calling |> > an error function, *and* if the error function returns, somehow |> > returning something (a reference to a static variable, for example) so |> > that the program can continue, with the error memorized so that the user |> > can test it later. KH: |> Ok, let's follow this all the way through. Even if we make things easy, |> and assume that i and j are simple integer types rather than objects, |> we still have the very likely possibility that vec is a template class, |> making the result of vec[j++] an object of some parameterized type T. |> The Vec::operator[](int) function has to return a T or a reference to |> a T, which will get copied. How can one possibly test whether the |> copy was successful without making assumptions about what T actually is? |> Even if the author of Vec is very diligent in error handling and |> reporting, it is impossible to write a general Vec that handles |> errors generated by T. JK: This is in general a problem. If the possibility existed to define the error handler on a class (rather than template) basis, it would be possible to invalidate objects resulting from an out of bounds reference. I think, however, that this may be taking flexibility a little bit too far. My basic idea was that the program could somehow stumble on, with the error going (temporarily) undetected. The user error handler would simply set a global variable, and return. At the end of a long and complicated calculation, the global variable would be tested. This is based on the way IEEE handles things in their floating point specification. Normally, you can suppress most of the error reporting, and errors will generate special values which propagate. Then, at the end of a long calculation, you read the status to know if your results are valid. I do not think that this is generally a good error handling strategy, but there are cases where it is appropriate, and a really flexible class will give the user the possibility to implement it. Similarly, I think that most (all?) IEEE implementations signal error conditions by default, and if you haven't done anything specific to handle the signal, your program aborts. You can also mask the signal, and test the status after each operation, if you want. JK: |> > (I'll admit that my array classes don't do this. They were originally |> > designed with the idea that a bounds check error aborted, and the |> > possibility of allowing the user to generate an exception was only added |> > very recently. The intent is that if you aren't sure of an index, and |> > what to continue even in case it is out of bounds, you check it before |> > indexing.) KH: |> So there are errors that can be generated in your array classes that will |> go unreported. I do not intend to pick on you, James, but my point is |> this: there are cases where there really is no good way to report errors |> without using exceptions. And given this, they must be used if we are |> to produce truly robust code. And given this, we must learn to use them |> effectively rather than trying to put off the inevitable. JK: Since I allow the user to throw an exception, yes. Previously, no. In the current implementation, returning from the error handler results in an abort, with an error message, but there are still ways of ignoring the error. For example, just encapsulate the expression: try { vec[ i ++ ] = vec[ j ++ ] ; } catch (...) {} Of course, at this point, the value of i and j, as well as the value of vec[ i ], are undefined. (Aren't side effects nice.) The only safe solution is to always abort, but as you have pointed out in previous postings, this is not a particularly flexible approach. Of course, allowing a user handler that does nothing, and just returns, makes ignoring the error even easier. But I suspect that with a bit of imagination, I could think of an application where even this would be appropriate. There is no perfect compromise between flexibility and safety. If you give enough rope, someone is going to hang themself. Depending on the application, you lean toward more safety, or more flexibility. For the moment, I really prefer the most absolute approach; a bounds check error aborts. But given the difficulty of ignoring this error with exceptions, I'm willing to allow the user that choice, too, if he prefers to make use of it.