What is considered best practice for aborting on errors in C?
In our code base we currently have a pattern using
#define CHECKERROR(code) if(code) { return code; }
but this leads to resources not being closed in code of the form
/* not actual code due to non-disclosure restrictions */
int somefunction() {
handle_t res1, res2;
int errorcode;
res1 = getResource();
res2 = getResource();
errorcode = action1(res1, res2);
CHECK(errorcode);
errorcode = action2(res1, res2);
CHECK(errorcode);
freeResource(res1);
freeResource(res2);
return errorcode;
}
I came across the pattern
/* initialize resources */
do {
/* ... */
errorcode = action();
if(errorcode) break;
/* ... */
} while(0);
/* cleanup resources */
return errorcode;
before in articles, but couldn't find any source discussing it now.
What is a good practice, that would be considered idiomatic to C? Does the do { } while(0);
pattern qualify? Is there an idiomatic way to make it more clear, that it is not intended to be a loop, but a block with non-local exit?
What is considered best practice for aborting on errors in C?
What is a good practice, that would be considered idiomatic to C?
Really, nothing. There is no best-practice. Best is to tailor a specific solution to the specific case you are handling. For sure - concentrate on writing readable code.
Let's mention some documents. MISRA 2008 has the following. The rule is strict - single exit point. So you have to assign variables and jump to a single return
statement
Rule 6–6–5 (Required) A function shall have a single point of exit at the end of the function.
Error handling is the only place where using goto
is actually encouraged. Linux Kernel Coding style presents and encourages using goto
to "keep all exit points close". The style is not enforced - not all kernel functions use this. See Linux kernel coding style # Centralized exiting of functions.
The kernel recommendation of goto
was adopted by SEI-C: MEM12-C. Consider using a goto chain when leaving a function on error when using and releasing resources.
Does the do { } while(0); pattern qualify?
Sure, why not. If you do not allocate any more resources inside the do { .. here .. }while(0)
block, you might as well write a separate function and then call return
from it.
There are also expansions on the idea. Even implementations of exceptions in C using longjmp
. I know of ThrowTheSwitch/CException.
Overall, error handling in C is not easy. Handling errors from multiple libraries becomes extremely hard and is an art of its own. See MBed OS error-handling, mbed_error.h, even a site that explains MBed OS error codes.
Strongly prefer single return point from your functions - as you found out, using your CHECK(errorcode);
will leak resources. Multiple return
places are confusing. Consider using goto
s:
int somefunction() {
int errorcode = 0;
handle_t res1 = getResource();
if (!res1) {
errorcode = somethnig;
goto res1_fail;
}
handle_t res2 = getResource();
if (!res2) {
errorcode = somethnig_else;
goto res2_fail;
}
errorcode = action1(res1, res2);
if (!errorcode) {
goto actions_fail;
}
errorcode = action2(res1, res2);
if (!errorcode) {
goto actions_fail;
}
actions_fail:
freeResource(res2);
res2_fail:
freeResource(res1);
res1_fail:
return errorcode;
}