i know the general answer to using goto is always "No!". I understand why, especially when teaching basics to new programmers, because it can lead to very bad practices and horrible spaghetti code.

but ...

when i write certain hardware drivers, i have to immediately cease executing a function if an error occurs, and restore the hardware to a known state in a manner dependent upon whether and what type of an error occurred. So i find that careful use of goto can be convenient when used as part of a hardware error-checking macro

#define ErrorCheck(arg)  { errCode = (arg); if (errCode < 0) goto EXIT; }

int MyDriver_driverFunction(int whatever, char * something)
{
   int errCode = ERROR_CLEAR;   // zero (0) is no error
   // ...

   ErrorCheck( subfunction(val1, val2) );
   // ...
   
   for (port = 0; port < MAX_NUM_PORTS; port++)
   {
      ErrorCheck( anotherfunction(port, &status) );
      switch(whatever)
         // ...
   }
   ErrorCheck( somethingElse(command, response, something) );
   // ...

EXIT:
   if (errCode < 0)
      // hardware error-specific exit routines ... 
   else 
      // standard exit routines ...

   return errCode;
}

the alternative to doing it without use of gotos seems to be putting the entire function within a conditional loop that will break out early on the condition that an error is detected. I'm not sure that this is better. it looks more clunky.

int MyDriver_driverFunction(int whatever, char * something)
{
   int errCode = ERROR_CLEAR;   // zero (0) is no error
   // ...

   while (1)
   {  
      if ((errCode = subfunction(val1, val2)) < 0) 
         break;
      // ...
   
      for (port = 0; port < MAX_NUM_PORTS; port++)
      {
         if ((errCode = anotherfunction(port, &status)) < 0) 
            break;   // will just get out of for loop

         switch(whatever)
            // ...
      }
      if (errCode < 0)   // must have another break to get out of while(1)
         break;

      if ((errCode = somethingElse(command, response, something)) < 0) 
         break;
      // ...

      break;   // must end while(1) if all functions succeeded
   }

   if (errCode < 0)
      // hardware error-specific exit routines ...
   else 
      // standard exit routines ...

   return errCode;
}

The two methods are practically the same from a logic perspective, but i think the first way looks and reads better. and is IMO easier to debug and maintain.

However, these programs will stick around for years with potentially multiple revisions by different engineers over time. If i do the first way, i invariably have to defend the use of "goto" to every other person who comes along wondering WTF i'm doing with gotos in my program, or worse, that they'll just think i'm an idiot and talk smack about me behind my back because i violated the "No Goto" rule.


what do you people think?


.

Edited 6 Years Ago by jephthah: n/a

Did you try investigating setjmp, longjmp.

Also sometimes, for efficiency, goto's are the best solution.

Edited 6 Years Ago by gerard4143: n/a

I found setjmp and longjmp quite useful in database programs to quickly return errors between deeply nested function calls. c++ exceptions are the alternative in c++ programs. goto is only useful within the same function. setjmp and longjmp are between functions, although could be used within the same function too.

yeah, i try not to jump between functions, this is just to get out of any single function cleanly in the case of a hardware error.

i guess im just soliciting opinions between doing it the first way (with gotos) or the second (without). or another way i havent considered. i'd rather not use gotos because of the stigma attached to them, but i'm not seeing a cleaner way at the moment.

yeah, i try not to jump between functions, this is just to get out of any single function cleanly in the case of a hardware error.

i guess im just soliciting opinions between doing it the first way (with gotos) or the second (without). or another way i havent considered. i'd rather not use gotos because of the stigma attached to them, but i'm not seeing a cleaner way at the moment.

I don't see anything wrong with gotos here. If you are forced to not use them, then in this particular case you can split the functionality into a worker function and a setup-cleanup wrapper, something along the lines of

int MyDriver_workerFunction(int whatever, char * something)
{
   int errCode = ERROR_CLEAR;   // zero (0) is no error
      if ((errCode = subfunction(val1, val2)) < 0) 
        return errCode;
      // ...
   
      for (port = 0; port < MAX_NUM_PORTS; port++)
      {
         if ((errCode = anotherfunction(port, &status)) < 0) 
            return errCode;

         switch(whatever)
            // ...
      }
      if ((errCode = somethingElse(command, response, something)) < 0) 
         return errCode;
   //...
    return errCode;
}
 int MyDriver_driverFunction(int whatever, char * something)
{
   errCode = MyDriver_workerFunction(whatever, something);
   if (errCode < 0)
      // hardware error-specific exit routines ...
   else 
      // standard exit routines ...

   return errCode;
}

yeah, that's another way. i don't care for having multiple returns throughout a function, but that's a style point probably.

i guess it all comes down to style, really.

no, we're not "forced" to write programs one way or another. but some people are so hammered in with "no gotos" (especially a couple senior old-timers im thinking of) that they just can't abide any exception, and they just complain loudly and make a big deal about it.

im pretty happy with sparse use of gotos in a clean macro, as long as it can be rationalized. in this case, exception handling is necessary and i believe it makes it more maintainable and easy to debug.

I guess i was just trying to see if someone could pose a rational explanation of why I shouldnt do it with gotos, and give an elegant alternative.

I guess i was just trying to see if someone could pose a rational explanation of why I shouldnt do it with gotos, and give an elegant alternative.

That would be extremely hard. Any rationale against goto stems this way or other from the original Dijkstra article. The point of the article is very valid, yet not applicable to this case. Your approach does not violate the sequentiality of the flow, period.

Comments
well-put. i can live with that :)
This question has already been answered. Start a new discussion instead.