Discussion
Team LiB
Previous Section Next Section

Discussion

From this Item onward, this section focuses on dealing with run-time errors that are not due to faulty coding internal to a module or subsystem. (As Item 68 covers separately, prefer to use assertions to flag internal programming errors, ones that are just outright coding errors on some programmer's part.)

Determine an overall error reporting and handling policy for your application and for each module or subsystem, and stick to it. Include a policy for at least each of the following points.

Universally:

  • Error identification: For each entity (e.g., each function, each class, each module), document the entity's internal and external invariants.

For each function:

  • Error identification: For each function, document its preconditions and postconditions, the invariants it shares responsibility for maintaining, and the error-safety guarantee it supports. (See Items 70 and 71.) Note that destructors and deallocation functions in particular must always be written to support the no-fail guarantee, because otherwise it's often impossible to reliably and safely perform cleanup (see Item 51).

For each error (see the definition of "error" in Item 70):

  • Error severity and categorization: For each error, identify a severity level. Preferably provide a way to fine-tune diagnostics for particular error categories and levels to facilitate remote user assistance.

  • Error detection: For each error, document which code is responsible for detecting which error, following the advice of Item 70.

  • Error handling: For each error, identify the code that is responsible for handling the error, following the advice in Item 74.

  • Error reporting: For each error, identify appropriate reporting method(s). These commonly include recording the error in disk file logs, printed logs, electronic dump transmissions, or possibly inconvenient and annoying pager calls in the case of severe errors.

For each module:

  • Error propagation: For each module (note: each module, not each error), identify which coding mechanism will be used to propagate errors (e.g., C++ exceptions, COM exceptions, CORBA exceptions, return codes).

We emphasize that error handling strategies should change only on module boundaries (see Items 62 and 63). Each module should consistently use a single error handling strategy and mechanism internally (e.g., modules written in C++ should use exceptions internally; see Item 72) and consistently use a single, possibly different, error handling strategy and mechanism in its interface (e.g., the module might present a flat C API to accommodate callers that could be written in various languages, or a COM wrapper that presents COM exceptions).

All functions that are entry points into the module are directly responsible for translating from the internal to the external strategy if they are different. For example, in a module that uses C++ exceptions internally but presents a C API boundary, all C APIs must catch(…) all exceptions and translate them to error codes.

Note in particular that callback functions and thread mainlines by definition are (or can be) on a module boundary. Each callback function body and thread mainline body should translate its internal error mechanism to the appropriate interface error strategy (see Item 62).

    Team LiB
    Previous Section Next Section