Exception vs. error-code vs. assert

2019-01-22 00:00发布

问题:

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:

  1. invalid report no.
  2. invalid state (the report_generator is a FSM)
  3. no device is active
  4. 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? :)

回答1:

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".



回答2:

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).



回答3:

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.



回答4:

I recommend reading the Boost community guide [boost.org] to exceptions and error handling.



回答5:

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.



回答6:

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.



回答7:

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;
    }
}


回答8:

First - be consistent!

Second:

  • just true/false is not enough. It has to be combined with error codes (false + getLastError for example).
  • error-codes are fast, but build some infrastructure to convert them easily into strings.
  • assert/log: no, you want the application to be able to react on the error
  • exceptions are slower than error-codes, but easier to program with a difficult control flow.
  • combinations: only true/false + error codes combine, for the rest BE CONSISTENT which means: do not combine.


回答9:

  • logging should be used if you don't have access to a terminal for producing/reading error reports.
  • returning True/False should be combined with error codes. Example: A function returns True on success, False on error, and sets a variable (global or parameter, your choice) with an appropriate error code/description.
  • exceptions: in my opinion, it's good to combine them with logging and graceful recovery from errors. If this is not possible, you might as well resort to error codes, as exceptions then provide no additional benefits.
  • assert(): As others have pointed out it compiles away on release builds, so fire at will.

2c