I'm working on a large C application that I've divided into sub-libraries (e.g. networking, message handling, message building, etc.)
I've converged onto a pattern where functions are only allowed to return error codes, and all data must be returned via supplied pointers. This pattern has worked well because it is very easy to "bubble-up" errors that are generated from low-level.
I'm curious, are there other standard design patterns for dealing with errors in large C programs?
-
1This post here may contain the answer you are looking for: In languages without exception-handling, should error codes be returned from the function or in function parameters? Not really a duplicate, I think.Doc Brown– Doc Brown2021年06月22日 15:08:56 +00:00Commented Jun 22, 2021 at 15:08
2 Answers 2
There are errors, and there are errors.
Programming-errors are either handled by assertions, which only fire in debug-mode, saving release-mode from the associated costs, or by immediate abort with an error-message. Trying to recover risks more damage to the users data and is thus generally inacceptable.
The possibility of outside interference (radiation, wear and tear on the computing system) argues for more checking of the internal consistency left in the release version, and using a supervisor process to restart if needed.
Somewhat expected outside errors, like insufficient resources, needed resources denied (cannot gain sufficient access), input errors, and errors from peers, servers, and clients on the other hand generally have to be handled gracefully. The main ways for doing that are:
Handle it yourself. For example retry, wait, or try alternative. Often cannot be done, and certainly degrades the control the caller has.
Notify the caller. Let him handle that in any appropriate way.
- If the return-value has some invalid states (often null-pointer, negative integers, and the like) use one of those, and optionally store additional error-info elsewhere (see
errno
). - Return an error-code, data has to be returned through out or in-out parameters. Might be a bit more cumbersome, but can be applied uniformly.
- Return a structure combining error-code and return-data. Especially popular in functional languages, and gained more popularity with Rust and other languages eschewing exceptions.
- Use exceptions. Not directly applicable to C, though take a look at
setjmp
and longjmp. It has been done before, for example in the implementation of the Lua interpreter. - Use a callback if an error is encountered. This is also often used for periodic status-information in a long-running possibly non-asynchronous operation to allow feedback to the user. The infamous Annex K goes that route. And one of the important criticisms is the use of the callback, especially that it isn't even thread-local, not being passed every call is somewhat easier to digest.
- If the return-value has some invalid states (often null-pointer, negative integers, and the like) use one of those, and optionally store additional error-info elsewhere (see
-
All true, maybe helpful for the OP, but not really an answer to the question from the title, I think.Doc Brown– Doc Brown2021年06月22日 15:20:34 +00:00Commented Jun 22, 2021 at 15:20
-
@DocBrown Paraphrased: There are multiple (more or less) standard patterns, appropriate in different circumstances, and here they are. Which implicitly answers the question whether there is one with no, multiple. Also, the question in the body asks explicitly for that.Deduplicator– Deduplicator2021年06月22日 15:54:44 +00:00Commented Jun 22, 2021 at 15:54
Depending on your usecase it may be beneficial to use assertions wherever you are absolutely 100% sure that further execution of the program makes no sense or is dangerous. They can be used as a way to immediately tell you if things have gone bad, where they have gone bad, and give you first hints on where to start looking for the underlying error.
People make the plausible point that assertions should never end up in production code, and I would only advise them in the developement/testing phase. You can then omit them via compiler flag for your production code.
-
5Using assertions is more debugging than error handling.Doc Brown– Doc Brown2021年06月22日 14:29:37 +00:00Commented Jun 22, 2021 at 14:29