|
Boost : |
From: scleary_at_[hidden]
Date: 2002-11-20 16:02:59
> Has anyone run into a comprehensive attack on these and similar exception
> class problems? Is there a better way than each Boost developer just
> hacking together individual exception classes? Could we do better with a
> Boost exception class or idiom?
I can only speak from my own personal experience, here...
In my own code, a base class has evolved over time, which I call simply
'error' (in my own namespace, of course). It has the following properies:
. Publicly derives from std::exception.
. Contains a std::string member containing the exception message (msg_).
msg_.c_str() is returned to satisfy std::exception::what(). A constant
reference to msg_ is returned from a member function message(); this is
provided as a convenience.
. Contains another std::string member containing an xml description of the
exception. A constant reference to this member is returned from the member
function xml().
. The constructor is declared protected, and has the following signature:
error(const string & type, const string & message,
const string & funcname = string(), const string & code = string())
Furthermore, I have evolved the following practices:
. The "exception message" is always of the form:
[ErrorTypeName] error `[ErrorMessage]'
with an optional postfix:
from `[FunctionName]'
. The "xml description" is of the form:
<error type=[ErrorTypeName] message=[ErrorMessage] function=[FunctionName]
code=[ErrorCode]/>
Though this is a loose definition; 'function' and 'code' are optional,
'error's may be nested arbitrarily, and additional attributes may be added.
It is always guaranteed to be valid XML, however.
. The "[FunctionName]" is intended to be the name of the OS/library function
that returned an error; but whenever possible additional runtime information
should be included in parenthesis after the actual function name, e.g.,
"CreateFile ([AttemptedFileName])" or "WriteFile (async callback)".
. The "[ErrorMessage]" is a string error taken from an OS/library lookup
function, specific to a derived error class. It should be trimmed (i.e., no
trailing whitespace), but may contain whitespace (including newlines). If
the error code is not known by the lookup function, either "Unknown error
[ErrorCodeDec]" or "Unknown error 0x[ErrorCodeHex]" should be used.
. There is a one-to-one mapping between derived error classes and values of
"[ErrorTypeName]".
The constructor for error() enforces the first two of the practices that
have evolved.
However, as I said in the beginning, this has evolved fully from my personal
experience:
. I regularly have to store errors on disk or send them to other programs,
and I find the xml member useful for this.
. These are designed for a hierarchy of error classes, each one responsible
for a separate API. For example, here's a partial listing:
error
Win32_error
COM_error
OLE_error
OLEDB_error
DirectX_error
socket_error
. The need for nested errors and extendable error attributes (i.e., most of
the complexity) came from my OLEDB_error derived class.
. In general, I want to capture as much information as reasonably possible.
Conventions have also evolved for the design of derived classes, but I don't
see any need to post those at this time.
For a Boost error base class or idiom, I would suggest at least the
following:
. A strict definition of the exception message
. Public derivation from std::exception for all exception classes
. Contains a member of type std::string for the exception message. It took
a while for this to evolve in my 'error' (due to performance and resource
allocation concerns), but I am convinced this is the way to go.
Moving beyond that is more tenatious ground; one of the main questions on my
mind is should an exception hierarchy be structured after API interfaces
(like mine), or after Boost libraries? I just don't want to see a lot of
rework where each Boost library would have their own Win32_error class or
format_message function. OTOH, structuring an exception hierarchy after API
interfaces makes perfect sense for an end-user executable program (like what
I write), but maybe not as much sense for a library?
Regardless of what exactly we end up with, I am in favor both of exception
message guidelines, and of a common Boost error base class providing
reasonable functionality.
-Steve
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk