What is a good design for a set of exception classes?
I see all sorts of stuff around about what exception classes should and shouldn't do, but not a simple design which is easy to use and extend that does those things.
Getting 1 and 4 to work together is the biggest issue I'm having, since any formatting and file output methods could potentially fail.
EDIT: So having looked at exception classes in several classes, and also in the question Neil linked to, it seems to be common practice to just completely ignore item 1 (and thus the boost recommendations), which seems to be a rather bad idea to me.
Anyway, I thought I'd also post the exception class I'm thinking of using.
class Exception : public std::exception { public: // Enum for each exception type, which can also be used // to determine the exception class, useful for logging // or other localisation methods for generating a // message of some sort. enum ExceptionType { // Shouldn't ever be thrown UNKNOWN_EXCEPTION = 0, // The same as above, but it has a string that // may provide some information UNKNOWN_EXCEPTION_STR, // For example, file not found FILE_OPEN_ERROR, // Lexical cast type error TYPE_PARSE_ERROR, // NOTE: in many cases functions only check and // throw this in debug INVALID_ARG, // An error occured while trying to parse // data from a file FILE_PARSE_ERROR, } virtual ExceptionType getExceptionType()const throw() { return UNKNOWN_EXCEPTION; } virtual const char* what()throw(){return "UNKNOWN_EXCEPTION";} }; class FileOpenError : public Exception { public: enum Reason { FILE_NOT_FOUND, LOCKED, DOES_NOT_EXIST, ACCESS_DENIED }; FileOpenError(Reason reason, const char *file, const char *dir)throw(); Reason getReason()const throw(); const char* getFile()const throw(); const char* getDir ()const throw(); private: Reason reason; static const unsigned FILE_LEN = 256; static const unsigned DIR_LEN = 256; char file[FILE_LEN], dir[DIR_LEN]; };
Point 1 is addressed since all strings are handled by copying to an internal, fixed size buffer (truncating if needed, but always null terminated).
Although that doesn't address point 3, however I think that point is most likely of limited use in the real world anyway, and could most likely be addressed by throwing a new exception if needed.
Exception-safe classes A class can help ensure its own exception safety, even when it is consumed by unsafe functions, by preventing itself from being partially constructed or partially destroyed. If a class constructor exits before completion, then the object is never created and its destructor will never be called.
The short answer is NO. You would throw an exception if the application can't continue executing with the bad data. In your example, the logic is to display an error message on the front end and Option 2 is the cleaner method for achieving this requirement.
The C programming language does not support exception handling nor error handling. It is an additional feature offered by C. In spite of the absence of this feature, there are certain ways to implement error handling in C. Generally, in case of an error, most of the functions either return a null value or -1.
Use a shallow hierarchy of exception classes. Making the hierarchy too deep adds more complexity than value.
Derive your exception classes from std::exception (or one of the other standard exceptions like std::runtime_error). This allows generic exception handlers at the top level to deal with any exceptions you don't. For example, there might be an exception handler that logs errors.
If this is for a particular library or module, you might want a base specific to your module (still derived from one of the standard exception classes). Callers might decide to catch anything from your module this way.
I wouldn't make too many exception classes. You can pack a lot of detail about the exception into the class, so you don't necessarily need to make a unique exception class for each kind of error. On the other hand, you do want unique classes for errors you expect to handle. If you're making a parser, you might have a single syntax_error exception with members that describe the details of the problem rather than a bunch of specialty ones for different types of syntax errors.
The strings in the exceptions are there for debugging. You shouldn't use them in the user interface. You want to keep UI and logic as separate as possible, to enable things like translation to other languages.
Your exception classes can have extra fields with details about the problem. For example, a syntax_error exception could have the source file name, line number, etc. As much as possible, stick to basic types for these fields to reduce the chance of constructing or copying the exception to trigger another exception. For example, if you have to store a file name in the exception, you might want a plain character array of fixed length, rather than a std::string. Typical implementations of std::exception dynamically allocate the reason string using malloc. If the malloc fails, they will sacrifice the reason string rather than throw a nested exception or crashing.
Exceptions in C++ should be for "exceptional" conditions. So the parsing examples might not be good ones. A syntax error encountered while parsing a file might not be special enough to warrant being handled by exceptions. I'd say something is exceptional if the program probably cannot continue unless the condition is explicitly handled. Thus, most memory allocation failures are exceptional, but bad input from a user probably isn't.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With