The libcult
exception handling library defines a base
exception type to be used by the rest of libcult
. It has
a very basic interface:
namespace Cult { namespace EH { class Exception: public virtual std::exception { public: virtual char const* what () const throw (); }; } }
It derives from std::exception
to allow catching
all exceptions with a single handler. Default implementation of the
what()
member function returns type-name of the exception.
Every non-trivial library in libcult derives its own base exception which all library-defined exceptions inherit. This way you can catch all exceptions from a library with one handler.
Sometimes it may seem convenient to further partition (by providing corresponding base classes) exceptions space into logic exceptions (shared by all implementations) and implementation exceptions. For example, if you pass an allocator illegal size 0 then the allocator throws an exception which can be classified as a logic exception. If, however, you passed valid size but there is not enough memory then the allocator throws an exception which can be classified as an implementation exception1.
The problem with this approach lies in the fact that someone's logic exception is someone else's implementation exception. Consider, for instance, a buffer type that is implemented in terms of our allocator. If the buffer happened to request a memory block of size 0 and let the exception propagate through the interface boundaries it is no longer a logic exception.
1 It can be argued that the
NoMemory
exception should rather be classified
as logic. However, let's assume there are allocator implementations
that have infinite memory.