I'm working on a library that generates reports of devices. The generate_report (const std::string& no)
member function can fail due to various reasons:
- invalid report no.
- invalid state (the
report_generator
is a FSM)
- no device is active
- error during report generation
Which error-handling mechanism is best for these errors?
- just return
true
or false
- return error code
- assert and log
- throw exception(s)
- any combination of the above
Some context information: the normal workflow is as following. The user activates a devices, chooses a report from a list and clicks on "generate".
EDIT: Thanks for the replies so far! For me it's clear now when to use asserts and when to do error-handling. As for error-handling, error codes and exceptions both have pros and cons. I think I go for exceptions (and create four classes for the above errors), but I'm not yet really convinced. I always thought of exceptions of 'unexpected situations'. An invalid report no isn't really unexpected. Any advice? :)
Any of these have different purposes:
error code vers. exception(s): exceptions and error codes represent different idioms of how result codes shout be handled. Exceptions are more robust - result codes can be ignored or lost. A library should usually strongly distinguish where/what exceptions are thrown, and when error codes are used. At best, use only one of both at all.
return true
or false
: A specialization of error codes.
Usually the worst idea - only good if there is no more to report than good or bad (i.e. malloc
returns either good or bad (= NULL
).
assert and log: These are debugging techniques, and should not be used as report mechanisms to users / clients. Asserts just say "something happened, that I can not handle - I quit".
assert is not the right choice. Use assert when you have an invariant; something that should never happen. Don't do things like assert() that an argument will never be null if it is an error condition and not an invariant.
If it were me, I would use exceptions in the interface and, if I had to, translate error codes by functions used internally if they do not use exceptions. Just be consistent about it (and don't use assert for this stuff).
Exceptions compared to true/false and error codes have several important advantages:
- Exceptions cannot be ignored. If your code throws an exception the caller has to catch it to avoid getting an unhandled exception.
- Exceptions can be handled at a higher level than the immediate caller. If you use error codes you may end up in situations where you at all layers of you application have to check for errors and pass them back to the caller.
Asserts are used to express stuff like preconditions in your code and will hopefully uncover any bugs during development. However, you should not rely on asserts in your release code, and for performance reasons asserts are normally removed from release code.
I recommend reading the Boost community guide [boost.org] to exceptions and error handling.
It is often a matter of taste what strategy to choose. I say pick up what best integrates with the clients of your library. If they adopt exception strategy, use exceptions. If they are accustomed to error codes, stick with it.
How reliable are the devices you are reporting on?
I ask because for a large class of devices not connected, not switched on, out of batteries, busy doing something else etc. etc. are fairly normal states.
If this is the case I would favour returning a status code (note not an error code ) if the device is somehow unavailable.
If on the other hand you consider these devices super reliable and it really is exceptional for them not to respond then exception handling may be the way to go.
It doesn't really matter that mutch as 'exceptions' are really just a fancy way to code 'if (x != 0) { goto error_routine; }, but, I personally prefer exception handling to deal with exceptional situations not routine events like end_of_file.
I'm going to go against the grain and suggest both error codes and exceptions, but only because you are making a library. Since you say you are making a library, I'm guessing that library will be made available to code written by people you have no control over. So, making your code friendly to different compilers and possibly even languages is a good thing.
So I would code a C++ exception library and provide header files detailing your exception classes. I would also code a C interface that handles the exceptions for the user. Now the user can link against which ever interface is appropriate:
#ifdef __cplusplus__
void generate_report(const std::string& rep_number, ostream& output);
extern "C"
#endif
int generate_report(const char* rep_number, const char* outputfilename,
int* error_code, char* error_text, int max_error_text_len);
The C implementation calls the C++ implementation:
extern "C"
int generate_report(const char* rep_number, const char* outputfilename,
int* error_code, char* error_text, int max_error_text_len)
{
ofstream os;
try {
os.open(outputfilename, IOS_WRITE);
generate_report(rep_number, os);
os.close();
return TRUE;
} catch (base_exception& e) {
os.close();
if (error_code) *error_code = e.error_code();
if (error_text) strncpy(error_text, e.str(), max_error_text_len);
return FALSE;
}
}