|Think about Loose Coupling|
As seems to happen to all Perl programmers eventually, I've been thinking about error handling in modules I code. It seems like I always start off just returning undef or using die, then after a while want to know more information about why something failed, and end up writing some kind of half-assed error handling system, which I gradually expand. And this seems to be what everybody else does, so you have to wade through documentation to know whether a module has custom error handling code, uses variables like $! or $@, or just dies. I usually end up just writing things like:
and plan on fixing it later, which only occasionally happens.
die and eval solve part of this problem, but it's annoying to have to wrap every constructor and method call inside of an eval block, and in general die should only be used for truly unexpected and exceptional conditions, not simple errors that may happen from time to time.
Realizing I was about to start coding another half-assed error handling system, I decided instead to try to put together something more flexible. My goals are:
I've posted the code for my fellow monks' consideration and comments. I used it for a module called Ekahau, so it's called Ekahau::ErrHandler; of course it would be renamed if I uploaded it to CPAN.
Interface for Module ProgrammersTo make this as simple as possible for module programmers, there are only 5 simple requirements:
User InterfaceIf an error is indicated by a constructor or method returning undef, the last error can be retreived with the lasterr method. If the error happened during a method call, you can get the last error for that object with $obj->lasterr; if the constructor failed, you can use Class->lasterr to get the last constructor error.
That means you can do:
CustomizingYou can customize the error handling at three levels: for a particular object, for a specific class, and for all classes which use Ekahau::ErrHandler. There are two types of customization currently supported.
First, you can use the set_errholder method or the ErrorHolder constructor argument to give a reference to a scalar where errors can be stored. This can make those die statements more readable, but more importantly can be used for threadsafe error handling, since it avoids using a global variable shared across all threads. For example:
Second, you can use set_errhandler to set your own error handler that should be called when a module encounters an error. For example, if you prefer to use eval/die for your error handling, you could use:
to get that effect for all modules using Ekahau::ErrHandler.