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.
- The exception classes shouldn't throw exceptions, since this could lead straight to the termination of the process without any chance to log the error etc.
- It needs to be possible to get a user friendly string, preferable localised to their language, so that there's something to tell them before the application terminates itself if it cant recover from an error.
- It needs to be possible to add information as the stack unwinds, eg if an xml parser fails to parse an input stream, to be able to add that the source was from a file, or over the network, etc.
- Exception handlers need easy access to the information they need to handle the exception
- Write formatted exception information to a log file (In English, so no translations here).
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 serveral 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 recomendations), which seems to be a rather bad idea to me.
Anyway I thought id 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 determin
//exception class, useful for logging or other localisation methods
//for generating a message of some sort.
enum ExceptionType
{
//shouldnt ever be thrown
UNKNOWN_EXCEPTION = 0,
//same as above but has a string that may provide some info
UNKNOWN_EXCEPTION_STR,
//eg 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, cosnt 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 doest address point 3, however I think that point is most likly of limited use in the real world anyway, and could most likely be addressed by throwing a new exception if needed.