Odds and ends: error reporting
Welcome back.
In this post, some old content from appendix A of the book, Odds and ends. Yes, this is what’s affectionately known as a filler post. It’s technical, rather than finance-related; but it’s about a part of coding that everybody bumps into eventually, and I hope that you will find it interesting.
Follow me on Twitter if you want to be notified of new posts, or add me to your circles, or subscribe via RSS: the buttons for that are in the footer. Also, make sure to check my Training page.
Error reporting
There are a great many places in the library where some condition must be checked. Rather than doing it as
we wanted to express the intent more clearly, i.e., with a syntax like
where on the one hand, we write the condition to be satisfied and not its opposite; and on the other hand, terms such as require, ensure, or assert—which have a somewhat canonical meaning in programming—would tell whether we’re checking a precondition, a postcondition, or a programmer error.
We provided the desired syntax with macros. “Get behind thee”, I hear you say. True, macros have a bad name, and in fact they caused us a problem or two, as we’ll see below. But in this case, functions had a big disadvantage: they evaluate all their arguments. Many times, we want to create a moderately complex error message, such as:
If require
were a function, the message would be built whether or
not the condition is satisfied, causing a performance hit that would
not be acceptable. With a macro, the above is textually replaced by
something like:
which builds the message only if the condition is violated.
Listing A.3 shows the current version of one of the macros, namely,
QL_REQUIRE
; the other macros are defined in a similar way.
Listing A.3: Definition of the QL_REQUIRE
macro.
Its definition has a few more bells and whistles that might be
expected. Firstly, we use an ostringstream
to build the message
string. This allows one to use a syntax like
to build the message (you can see how that works by replacing the
pieces in the macro body). Secondly, the Error
instance is passed
the name of the current function as well as the line and file where
the error is thrown. Depending on a compilation flag, this information
can be included in the error message to help developers; the default
behavior is to not include it, since it’s of little utility for
users. Lastly, you might be wondering why we added an else
at the
end of the macro. That is due to a common macro pitfall, namely, its
lack of a lexical scope. The else
is needed by code such as
Without the else
in the macro, the above would not work as
expected. Instead, the else
in the code would pair with the if
in
the macro and the code would translate into
which has a different behavior.
As a final note, I have to describe a disadvantage of these macros. As they are now, they throw exceptions that can only return their contained message; no inspector is defined for any other relevant data. For instance, although an out-of-bounds message might include the passed index, no other method in the exception returns the index as an integer. Therefore, the information can be displayed to the user but would be unavailable to recovery code in catch clauses—unless one parses the message, that is; but that is hardly worth the effort. There’s no planned solution at this time, so drop us a line if you have one.