How to structure code with multiple points of failure?
I have a piece of code that can fail at many points. With each additional action (which can also fail), readability decreases. Is there a better way to structure this? Is there a design pattern for this?
int32_t InitializeMyThingy(void) {
int32_t retcode;
retcode = DoA();
if (retcode != 0) return retcode;
retcode = DoB();
if (retcode != 0) return retcode;
retcode = DoC();
if (retcode != 0) return retcode;
retcode = DoD();
return retcode;
}
Alternatively (more readable?):
int32_t InitializeMyThingy(void) {
int32_t retcode;
retcode = DoA();
if (0 == retcode) {
retcode = DoB();
if (0 == retcode) {
retcode = DoC();
if (0 == retcode) {
retcode = DoD();
}
}
}
return retcode;
}
source to share
In C, the SESE (single input, single output) pattern is usually suitable with goto
:
int32_t InitializeMyThingy(void) {
int32_t retcode;
retcode = DoA();
if (retcode != 0) goto exit;
retcode = DoB();
if (retcode != 0) goto exit;
retcode = DoC();
if (retcode != 0) goto exit;
retcode = DoD();
exit:
return retcode;
}
By itself, this is no different from your first example, which has instructions return
after each check. But once your code starts allocating resources, the approach is goto
much cleaner:
int32_t InitializeMyThingy(void) {
int32_t retcode = E_FAIL;
A* a = NULL;
B* b = NULL;
C* c = NULL;
a = AllocateA();
if (a == NULL) goto exit;
b = AllocateB(a);
if (b == NULL) goto exit;
c = AllocateC(b);
if (c == NULL) goto exit;
retcode = DoSomething(c);
exit:
free(c);
free(b);
free(a);
return retcode;
}
and now you don't have to worry about what to clear at every single exit point.
Despite what many naysayers talk about goto
, when used correctly, it is the simplest form of error handling in C.
As far as readability from adding extra actions is concerned, I think separating actions with whitespace helps a lot. Or, if you really want compactness, you might consider taking action and checking for errors inline:
if ((retcode = DoA()) != 0) goto exit;
source to share
This is why other languages have try / catch. Unfortunately C doesn't have this feature, so your format (I personally prefer the first one) is probably the best way.
You can also look at the try catch the expression in the C .
source to share
There is no specific pattern for this, but I am using the following format to improve readability and avoid goto statements.
#define BREAK_ON_ERROR(ret_val) { if (0 != ret_val) break; }
int32_t InitializeMyThingy(void)
{
int32_t ret = 0;
do{
ret = DoA(); BREAK_ON_ERROR(ret) ;
ret = DoB(); BREAK_ON_ERROR(ret) ;
ret = DoC(); BREAK_ON_ERROR(ret) ;
ret = DoD(); BREAK_ON_ERROR(ret) ;
/*
Code executed Gracefully.
Do Successful handling here.
*/
return ret;
}while(0);
/*
Error Occurred.
Handle Error Here.
*/
return ret;
}
source to share