about dealing with unexpected, or unusual, return states of subroutines
usually subroutines have a main purpose. for example, dividing two numbers, or writing to a file. sometimes the main purpose can not be fulfilled, for example because:
states likes this tend to be considered errors.
it can be useful to think in terms of operations that can not fail (for example, addition; as long as the hardware and compiler works) and operations that can fail (for example, memory allocation)
for example
for example
pro
con
for example
for example
the result will usually be a valid return type for proceeding, and may be less likely to be incompatible with subsequent computation.
pro
con
for example
the additional values still have to be unpacked syntactically, which needs additional forms to create and accept multiple return values. the position of the error value in the collection of output values has to be agreed on between different libraries for consistency. similar to returning a single compound value or special error type. for every subroutine call and return, code has to be added to check for an error status and it becomes a kind of manual flow control, with code that makes a call and checks if it shall continue. this interrupts subroutine composition
the error status can be passed as the subroutine result or output argument (or additional value). compare the following two signatures
input-value ... output-value ... output-status -> value input-value ... output-value ... -> status
the latter should be simpler, because there is always a status value, and only one, and the value is not mixed inbetween other output values
with error argument
con
with subroutine selection
not exiting the subroutine but continuing at a place at the end of the subroutine where errors are handled. cleanup, like deallocation before exit, can be done at this place. a local variable can be used to save error details where the error occurs. local jumps can also exit loops
pro
error information can be collected and subroutines exited in the call stack upwards using non-local jumps until a catcher is met that handles the error. caller/callee return semantics are disregarded. this corresponds to exception handling using throw and catch. with exception handling, typically a backtrace is collected when an error occurs and a default catcher that exits the program is installed. exception handling can also be implemented with delimited continuations
cleanup (heap-memory, file handles or similar) might still be necessary on every non-local exit point. garbage collection helps with the memory management at least
pro
con
a global variable that is set to specific values on error. the variable needs to be thread local for mult-threaded error handling. the value persists between routine calls and needs to be reset. this might be harder to optimize by the compiler since the value can be changed from many places
if multiple libraries use integer values for error ranges then error values of different libraries might overlap and be ambiguous. for this reason, it can become necessary to keep additional information with error objects, for example a string identifier for the library it belongs to (using enums would again be too likely to conflict)
error reporting is collecting details that may be helpful to find the cause and making this information available to the user. for example, a message written to standard output or a log file
information that can be useful when handling an error:
error-name error-description source-routine-name source-line-number source-file source-module-name backtrace literal-irritant-expression
the shell && operator checks exit codes:
mkdir new_directory && cd new_directory
most library functions return a special value to indicate that they have failed. the special value is typically -1, a null pointer, or a constant such as eof that is defined for that purpose. but this return value tells you only that an error has occurred. to find out what kind of error it was, one needs to look at the error code stored in the variable errno
uses a struct for error integer identifier and string group. has helpers, that subroutine results can be passed to, that check the return value and go to the error label on error status. sph-sc-lib
int main() { status_declare; // code ... status_require(test()); // more code ... exit: return status.id; }