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;
}

      

+3


source to share


4 answers


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;

      

+3


source


I prefer something like this when it is simple error checking without the need for elses.



int32_t InitializeMyThingy(void) 
{
    int32_t           retcode = DoA();
    if (0 == retcode) retcode = DoB();
    if (0 == retcode) retcode = DoC();
    if (0 == retcode) retcode = DoD();

    return retcode;
}

      

0


source


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 .

-2


source


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;
}

      

-2


source







All Articles