107

This question is actually a result of an interesting discussion at programming.reddit.com a while ago. It basically boils down to the following code:

int foo(int bar)
{
    int return_value = 0;
    if (!do_something( bar )) {
        goto error_1;
    }
    if (!init_stuff( bar )) {
        goto error_2;
    }
    if (!prepare_stuff( bar )) {
        goto error_3;
    }
    return_value = do_the_thing( bar );
error_3:
    cleanup_3();
error_2:
    cleanup_2();
error_1:
    cleanup_1();
    return return_value;
}

The usage of goto here appears to be the best way to go, resulting in the cleanest and most efficient code of all possibilities, or at least so it seems to me. Quoting Steve McConnell in Code Complete:

The goto is useful in a routine that allocates resources, performs operations on those resources, and then deallocates the resources. With a goto, you can clean up in one section of the code. The goto reduces the likelihood of your forgetting to deallocate the resources in each place you detect an error.

Another support for this approach comes from the Linux Device Drivers book, in this section.

What do you think? Is this case a valid use for goto in C? Would you prefer other methods, which produce more convoluted and/or less efficient code, but avoid goto?

Arnaud
  • 3,765
  • 3
  • 39
  • 69
Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
  • http://stackoverflow.com/questions/3339946/how-to-avoid-long-chain-of-frees-or-deletes-after-every-error-check-in-c/3339958#3339958 – karlphillip Nov 28 '11 at 20:53
  • @Eli: Why don't you remove the tags and place the function(cleanup_3();) in the parenthesis of if? –  Dec 05 '11 at 13:47
  • @Akito: what do you mean? Could you post your suggestion as an answer with a complete code sample? – Eli Bendersky Dec 06 '11 at 02:54
  • One of the things I hated most from Visual Basic (VBS and VB6 included) was the `on error goto error` error handling system :) – Manu343726 Sep 14 '13 at 12:12
  • You are missing a key point in this example: access to the resources you are trying to cleanup. The most common case is having the resources defined in the same scope as where you are placing the gotos, making the use of goto even more necessary, since they wouldn't even be available at cleanup_x() scopes. The finalizer block (labels) having access to the scope where the resources are defined, is what actually makes it the one of the best solutions compared to anything else. Gotos in these cases are clean, fast, readable, and maintainable. – drakorg Dec 23 '14 at 12:13
  • ... And for this reason cleanup is normally made right in place, and not delegated to a function. – drakorg Dec 23 '14 at 12:20
  • I am seriously surprised of the answers to this question. Anyone who thinks that `goto` is bad, should go and read the original articles that said so. When `goto` was bad, it was used in a way that made programs pretty confusing. It is no longer the case, so goto is no longer bad. – Iharob Al Asimi Nov 11 '21 at 02:14

16 Answers16

80

FWIF, I find the error handling idiom you gave in the question's example to be more readable and easier to understand than any of the alternatives given in the answers so far. While goto is a bad idea in general, it can be useful for error handling when done in a simple and uniform manner. In this situation, even though it's a goto, it's being used in well-defined and more or less structured manner.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • 1
    Can't he just remove those tags and put the functions directly into if?Won't it be more readable? –  Dec 05 '11 at 13:49
  • 11
    @StartupCrazy, I know this is years old, but for the sake of validity of posts on this site i will point out that no he can not. If he gets an error at goto error3 in his code he would run clean up 1 2 and 3, in your sugeested solution he would only run cleanup 3. He could nest things, but that would just be the arrow antipattern, something programmers should avoid. – gbtimmon Jun 11 '12 at 17:05
21

As a general rule, avoiding goto is a good idea, but the abuses that were prevalent when Dijkstra first wrote 'GOTO Considered Harmful' don't even cross most people's minds as an option these days.

What you outline is a generalizable solution to the error handling problem - it is fine with me as long as it is carefully used.

Your particular example can be simplified as follows (step 1):

int foo(int bar)
{
    int return_value = 0;
    if (!do_something(bar)) {
        goto error_1;
    }
    if (!init_stuff(bar)) {
        goto error_2;
    }
    if (prepare_stuff(bar))
    {
        return_value = do_the_thing(bar);
        cleanup_3();
    }
error_2:
    cleanup_2();
error_1:
    cleanup_1();
    return return_value;
}

Continuing the process:

int foo(int bar)
{
    int return_value = 0;
    if (do_something(bar))
    {   
        if (init_stuff(bar))
        {
            if (prepare_stuff(bar))
            {
                return_value = do_the_thing(bar);
                cleanup_3();
            }
            cleanup_2();
        }
        cleanup_1();
    }
    return return_value;
}

This is, I believe, equivalent to the original code. This looks particularly clean since the original code was itself very clean and well organized. Often, the code fragments are not as tidy as that (though I'd accept an argument that they should be); for example, there is frequently more state to pass to the initialization (setup) routines than shown, and therefore more state to pass to the cleanup routines too.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 29
    Yep, the nested solution is one of the viable alternatives. Unfortunately it becomes less viable as the nesting level deepens. – Eli Bendersky Apr 25 '09 at 14:17
  • 5
    @eliben: Agreed - but the deeper nesting might be (probably is) an indication that you need to introduce more functions, or have the preparation steps do more, or otherwise refactor your code. I could argue that each of the prepare functions should do their setup, call the next in the chain, and do their own cleanup. It localizes that work - you might even save on the three cleanup functions. It also depends, in part, on whether any of the setup or cleanup functions are used (usable) in any other calling sequence. – Jonathan Leffler Apr 25 '09 at 14:22
  • 9
    Unfortunately this doesn't work with loops -- if an error occurs inside a loop, then a goto is much, much cleaner than the alternative of setting and checking flags and 'break' statements (which are just cleverly disguised gotos). – Adam Rosenfield Apr 26 '09 at 05:19
  • Yes but gotos ruin your static code analysis tools! Or maybe you aren't using static code analysis tools? Shame on you. Not using static code analysis tools is like driving without a seat belt! – Trevor Boyd Smith Apr 27 '09 at 19:57
  • 2
    @Smith, More like driving without a bullet-proof vest. – strager Apr 27 '09 at 20:00
  • 1
    The introduction of additional functions would also address the loop issue; have the loop as the top-most item in the function and use a return statement to break out of it in case of error. Once everything is compiled, you'll end up with the same basic result, a branch when there's an error, but it's a bit more declarative and easier to reason about. – Richard Apr 27 '09 at 21:34
  • 1
    @Trevor: It's a poor static code analysis tool that is fazed by goto. Indeed, it is goto-heavy code that is likely to need analysis… – Donal Fellows May 22 '10 at 22:30
  • 9
    I know I'm necromancing here, but I find this advice rather poor - the [arrow anti-pattern](http://c2.com/cgi/wiki?ArrowAntiPattern) should be avoided. – KingRadical Nov 06 '13 at 10:33
  • @Richard that would be my method of choice as well, to use returns in small functions to emulate exceptions and to prevent "code exits screen right" nesting but keep things clean (mainly because you get an opportunity to introduce a name, a test, a clearly-scoped, informative comment, a chance to reduce the set of values in-scope, etc). Sometimes, though, the result can be a mess, lots of things called "thing()" "try_do_thing()" "do_thing()" "real_do_thing()" etc. In that case, end-of-function goto resource unwinding can end up neater. – Dan Sheppard Feb 04 '22 at 02:17
18

I'm surprised nobody has suggested this alternative, so even though the question has been around a while I'll add it in: one good way of addressing this issue is to use variables to keep track of the current state. This is a technique that can be used whether or not goto is used for arriving at the cleanup code. Like any coding technique, it has pros and cons, and won't be suitable for every situation, but if you're choosing a style it's worth considering - especially if you want to avoid goto without ending up with deeply nested ifs.

The basic idea is that, for every cleanup action that might need to be taken, there is a variable from whose value we can tell whether the cleanup needs doing or not.

I'll show the goto version first, because it is closer to the code in the original question.

int foo(int bar)
{
    int return_value = 0;
    int something_done = 0;
    int stuff_inited = 0;
    int stuff_prepared = 0;


    /*
     * Prepare
     */
    if (do_something(bar)) {
        something_done = 1;
    } else {
        goto cleanup;
    }

    if (init_stuff(bar)) {
        stuff_inited = 1;
    } else {
        goto cleanup;
    }

    if (prepare_stuff(bar)) {
        stufF_prepared = 1;
    } else {
        goto cleanup;
    }

    /*
     * Do the thing
     */
    return_value = do_the_thing(bar);

    /*
     * Clean up
     */
cleanup:
    if (stuff_prepared) {
        unprepare_stuff();
    }

    if (stuff_inited) {
        uninit_stuff();
    }

    if (something_done) {
        undo_something();
    }

    return return_value;
}

One advantage of this over some of the other techniques is that, if the order of the initialisation functions is changed, the correct cleanup will still happen - for instance, using the switch method described in another answer, if the order of initialisation changes, then the switch has to be very carefully edited to avoid trying to clean up something wasn't actually initialised in the first place.

Now, some might argue that this method adds a whole lot of extra variables - and indeed in this case that's true - but in practice often an existing variable already tracks, or can be made to track, the required state. For example, if the prepare_stuff() is actually a call to malloc(), or to open(), then the variable holding the returned pointer or file descriptor can be used - for example:

int fd = -1;

....

fd = open(...);
if (fd == -1) {
    goto cleanup;
}

...

cleanup:

if (fd != -1) {
    close(fd);
}

Now, if we additionally track the error status with a variable, we can avoid goto entirely, and still clean up correctly, without having indentation that gets deeper and deeper the more initialisation we need:

int foo(int bar)
{
    int return_value = 0;
    int something_done = 0;
    int stuff_inited = 0;
    int stuff_prepared = 0;
    int oksofar = 1;


    /*
     * Prepare
     */
    if (oksofar) {  /* NB This "if" statement is optional (it always executes) but included for consistency */
        if (do_something(bar)) {
            something_done = 1;
        } else {
            oksofar = 0;
        }
    }

    if (oksofar) {
        if (init_stuff(bar)) {
            stuff_inited = 1;
        } else {
            oksofar = 0;
        }
    }

    if (oksofar) {
        if (prepare_stuff(bar)) {
            stuff_prepared = 1;
        } else {
            oksofar = 0;
        }
    }

    /*
     * Do the thing
     */
    if (oksofar) {
        return_value = do_the_thing(bar);
    }

    /*
     * Clean up
     */
    if (stuff_prepared) {
        unprepare_stuff();
    }

    if (stuff_inited) {
        uninit_stuff();
    }

    if (something_done) {
        undo_something();
    }

    return return_value;
}

Again, there are potential criticisms of this:

  • Don't all those "if"s hurt performance? No - because in the success case, you have to do all of the checks anyway (otherwise you're not checking all the error cases); and in the failure case most compilers will optimise the sequence of failing if (oksofar) checks to a single jump to the cleanup code (GCC certainly does) - and in any case, the error case is usually less critical for performance.
  • Isn't this adding yet another variable? In this case yes, but often the return_value variable can be used to play the role that oksofar is playing here. If you structure your functions to return errors in a consistent way, you can even avoid the second if in each case:

    int return_value = 0;
    
    if (!return_value) {
        return_value = do_something(bar);
    }
    
    if (!return_value) {
        return_value = init_stuff(bar);
    }
    
    if (!return_value) {
        return_value = prepare_stuff(bar);
    }
    

    One of the advantages of coding like that is that the consistency means that any place where the original programmer has forgotten to check the return value sticks out like a sore thumb, making it much easier to find (that one class of) bugs.

So - this is (yet) one more style that can be used to solve this problem. Used correctly it allows for very clean, consistent code - and like any technique, in the wrong hands it can end up producing code that is long-winded and confusing :-)

psmears
  • 26,070
  • 4
  • 40
  • 48
  • Linus would probably reject your code https://blogs.oracle.com/oswald/entry/is_goto_the_root_of – Fizz Jul 14 '14 at 08:26
  • 1
    @user3588161: If he would, that's his prerogative - but I'm not sure, based on the article you linked to, that that's the case: note that in the style I'm describing, (1) the conditionals don't nest arbitrarily deeply, and (2) there are no additional "if" statements compared to what you need anyway (assuming you're going to check all the return codes). – psmears Jul 17 '14 at 10:52
  • So much this instead of the horrible goto and even worse arrow-antipattern solution! – The Paramagnetic Croissant Jan 23 '15 at 08:26
  • @Fizz Do you have the latest link? It no longer works. Oracle and Microsoft: If I had a penny each time they removed URLs, I would be a rich man! – kevinarpe Mar 15 '23 at 14:25
  • 1
    @kevinarpe: You can read the blog [here](https://web.archive.org/web/20150106215225/https://blogs.oracle.com/oswald/entry/is_goto_the_root_of), for what it's worth :) – psmears Mar 15 '23 at 14:28
  • @psmears Light speed! – kevinarpe Mar 15 '23 at 14:29
9

The problem with the goto keyword is mostly misunderstood. It is not plain-evil. You just need to be aware of the extra control paths that you create with every goto. It becomes difficult to reason about your code and hence its validity.

FWIW, if you look up developer.apple.com tutorials, they take the goto approach to error handling.

We do not use gotos. A higher importance is laid on return values. Exception handling is done via setjmp/longjmp -- whatever little you can.

dirkgently
  • 108,024
  • 16
  • 131
  • 187
  • 9
    While I've certainly used setjmp/longjmp in some cases where it was called for, I'd consider it even "worse" than goto (which I also use, somewhat less reservedly, when it is called for). The only times I use setjmp/longjmp are when either (1) The target is going to shut everything down in a way which is won't be affected by its present state, or (2) The target is going to reinitialize everything controlled within the setjmp/longjmp-guarded block in a way which is independent of its present state. – supercat Dec 06 '11 at 17:41
5

There's nothing morally wrong about the goto statement any more than there is something morally wrong with (void)* pointers.

It's all in how you use the tool. In the (trivial) case you presented, a case statement can achieve the same logic, albeit with more overhead. The real question is, "what's my speed requirement?"

goto is just plain fast, especially if you're careful to make sure that it compiles to a short jump. Perfect for applications where speed is a premium. For other applications, it probably makes sense to take the overhead hit with if/else + case for maintainability.

Remember: goto doesn't kill applications, developers kill applications.

UPDATE: Here's the case example

int foo(int bar) { 
     int return_value = 0 ; 
     int failure_value = 0 ;

     if (!do_something(bar)) { 
          failure_value = 1; 
      } else if (!init_stuff(bar)) { 
          failure_value = 2; 
      } else if (prepare_stuff(bar)) { 
          return_value = do_the_thing(bar); 
          cleanup_3(); 
      } 

      switch (failure_value) { 
          case 2: cleanup_2(); 
          case 1: cleanup_1(); 
          default: break ; 
      } 
} 
webmarc
  • 231
  • 2
  • 9
  • 1
    Could you present the 'case' alternative? Also, I would argue that this is way different from void*, which is required for any serious data structure abstraction in C. I don't think there's anyone seriously opposing void*, and you won't find a single large code base without it. – Eli Bendersky Apr 25 '09 at 13:34
  • Re: void*, that's exactly my point, there's nothing morally wrong with either. Switch/case example below. int foo(int bar) { int return_value = 0 ; int failure_value = 0 ; if (!do_something(bar)) { failure_value = 1; } else if (!init_stuff(bar)) { failure_value = 2; } else if (prepare_stuff(bar)) { { return_value = do_the_thing(bar); cleanup_3(); } switch (failure_value) { case 2: cleanup_2(); break ; case 1: cleanup_1(); break ; default: break ; } } – webmarc Apr 25 '09 at 14:52
  • 6
    @webmarc, sorry but this is horrible! Yo've just wholly simulated goto with labels - inventing your own non-descriptive values for labels and implementing goto with switch/case. Failure_value = 1 is cleaner than "goto cleanup_something" ? – Eli Bendersky Apr 25 '09 at 15:25
  • 5
    I feel like you set me up here... your question is for an opinion, and what I would prefer. Yet when I offered my answer, it's horrible. :-( As for your complaint about the label names, they're just as descriptive as the rest of your example: cleanup_1, foo, bar. Why are you attacking label names when that's not even germane to the question? – webmarc Apr 25 '09 at 16:47
  • 1
    I had no intention of "setting up" and cause any sort of negative feelings, sorry for that! It just feels like your new approach is targeted solely at 'goto removal', without adding any more clarity. It's as if you've reimplemented what goto does, just using more code which is less clear. This IMHO is not a good thing to do - getting rid of the goto just for the sake of it. – Eli Bendersky Apr 25 '09 at 16:59
  • Encapsulating the cleanup logic in a switch block (or any other block for that matter) allows additional logic to be executed between it and the if block above. This is far more maintainable, IMO, while the goto method is far more efficient. Since there's no clear goal for this code (speed versus maintainability, or any other parameters), there's no right answer here. – webmarc Apr 25 '09 at 17:24
  • @webmarc: Old answer, but his `goto` and your `case` example doesn't do the same thing. A `goto` will jump to the label and then just continue running from there. Your example runs the appropriate `case` branch and then runs whatever is after the `case` – remmy Oct 29 '13 at 21:32
  • I'm a little surprised nobody has pointed out that flaggy code, while an alternative to the goto, is actually less clean for two reasons. 1) You now have an unnecessary variable to keep track of. 2) The resulting code is more difficult to follow and understand than the example using `goto` in the question. – KingRadical Nov 06 '13 at 10:41
3

GOTO is useful. It's something your processor can do and this is why you should have access to it.

Sometimes you want to add a little something to your function and single goto let's you do that easily. It can save time..

toto
  • 880
  • 11
  • 21
  • 3
    You don't need access to every single thing your processor can do. Most of the time goto is more confusing than the alternative. – David Thornley Apr 27 '09 at 19:57
  • @DavidThornley: Yes, you _do_ need access to every single thing your processor can do, otherwise, you are wasting your processor. Goto is the best instruction in programming. – Ron Maimon Feb 21 '15 at 03:17
2

In general, I would regard the fact that a piece of code could be most clearly written using goto as a symptom that the program flow is likely more complicated than is generally desirable. Combining other program structures in weird ways to avoid the use of goto would attempt to treat the symptom, rather than the disease. Your particular example might not be overly difficult to implement without goto:

  do {
    .. set up thing1 that will need cleanup only in case of early exit
    if (error) break;
    do
    {
      .. set up thing2 that will need cleanup in case of early exit
      if (error) break;
      // ***** SEE TEXT REGARDING THIS LINE
    } while(0);
    .. cleanup thing2;
  } while(0);
  .. cleanup thing1;

but if the cleanup was only supposed to happen when the function failed, the goto case could be handled by putting a return just before the first target label. The above code would require adding a return at the line marked with *****.

In the "cleanup even in normal case" scenario, I would regard the use of goto as being clearer than the do/while(0) constructs, among other things because the target labels themselves practically cry out "LOOK AT ME" far moreso than the break and do/while(0) constructs. For the "cleanup only if error" case, the return statement ends up having to be in just about the worst possible place from a readability standpoint (return statements should generally be either at the beginning of a function, or else at what "looks like" the end); having a return just before a target label meets that qualification much more readily than having one just before the end of a "loop".

BTW, one scenario where I sometimes use goto for error-handling is within a switch statement, when the code for multiple cases shares the same error code. Even though my compiler would often be smart enough to recognize that multiple cases end with the same code, I think it's clearer to say:

 REPARSE_PACKET:
  switch(packet[0])
  {
    case PKT_THIS_OPERATION:
      if (problem condition)
        goto PACKET_ERROR;
      ... handle THIS_OPERATION
      break;
    case PKT_THAT_OPERATION:
      if (problem condition)
        goto PACKET_ERROR;
      ... handle THAT_OPERATION
      break;
    ...
    case PKT_PROCESS_CONDITIONALLY
      if (packet_length < 9)
        goto PACKET_ERROR;
      if (packet_condition involving packet[4])
      {
        packet_length -= 5;
        memmove(packet, packet+5, packet_length);
        goto REPARSE_PACKET;
      }
      else
      {
        packet[0] = PKT_CONDITION_SKIPPED;
        packet[4] = packet_length;
        packet_length = 5;
        packet_status = READY_TO_SEND;
      }
      break;
    ...
    default:
    {
     PACKET_ERROR:
      packet_error_count++;
      packet_length = 4;
      packet[0] = PKT_ERROR;
      packet_status = READY_TO_SEND;
      break;
    }
  }   

Although one could replace the goto statements with {handle_error(); break;}, and although one could use a do/while(0) loop along with continue to process the wrapped conditional-execute packet, I don't really think that's any clearer than using a goto. Further, while it might be possible to copy out the code from PACKET_ERROR everywhere the goto PACKET_ERROR is used, and while a compiler might write out the duplicated code once and replace most occurrences with a jump to that shared copy, the using the goto makes it easier to notice places which set the packet up a little differently (e.g. if the "execute conditionally" instruction decides not to execute).

supercat
  • 77,689
  • 9
  • 166
  • 211
2

I agree that the goto cleanup in reverse order given in the question is the cleanest way of cleaning things up in most functions. But I also wanted to point out that sometimes, you want your function to clean up anyway. In these cases I use the following variant if if ( 0 ) { label: } idiom to go to the right point of the cleaning up process:

int decode ( char * path_in , char * path_out )
{
  FILE * in , * out ;
  code c ;
  int len ;
  int res = 0  ;
  if ( path_in == NULL )
    in = stdin ;
  else
    {
      if ( ( in = fopen ( path_in , "r" ) ) == NULL )
        goto error_open_file_in ;
    }
  if ( path_out == NULL )
    out = stdout ;
  else
    {
      if ( ( out = fopen ( path_out , "w" ) ) == NULL )
        goto error_open_file_out ;
    }

  if( read_code ( in , & c , & longueur ) )
    goto error_code_construction ;

  if ( decode_h ( in , c , out , longueur ) )
  goto error_decode ;

  if ( 0 ) { error_decode: res = 1 ;}
  free_code ( c ) ;
  if ( 0 ) { error_code_construction: res = 1 ; }
  if ( out != stdout ) fclose ( stdout ) ;
  if ( 0 ) { error_open_file_out: res = 1 ; }
  if ( in != stdin ) fclose ( in ) ;
  if ( 0 ) { error_open_file_in: res = 1 ; }
  return res ;
 }
user1251840
  • 655
  • 5
  • 8
1

I personally am a follower of the "The Power of Ten - 10 Rules for Writing Safety Critical Code".

I will include a small snippet from that text that illustrates what I believe to be a good idea about goto.


Rule: Restrict all code to very simple control flow constructs – do not use goto statements, setjmp or longjmp constructs, and direct or indirect recursion.

Rationale: Simpler control flow translates into stronger capabilities for verification and often results in improved code clarity. The banishment of recursion is perhaps the biggest surprise here. Without recursion, though, we are guaranteed to have an acyclic function call graph, which can be exploited by code analyzers, and can directly help to prove that all executions that should be bounded are in fact bounded. (Note that this rule does not require that all functions have a single point of return – although this often also simplifies control flow. There are enough cases, though, where an early error return is the simpler solution.)


Banishing the use of goto seems bad but:

If the rules seem Draconian at first, bear in mind that they are meant to make it possible to check code where very literally your life may depend on its correctness: code that is used to control the airplane that you fly on, the nuclear power plant a few miles from where you live, or the spacecraft that carries astronauts into orbit. The rules act like the seat-belt in your car: initially they are perhaps a little uncomfortable, but after a while their use becomes second-nature and not using them becomes unimaginable.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Trevor Boyd Smith
  • 18,164
  • 32
  • 127
  • 177
  • 27
    The problem with this is that the usual way of totally banishing the `goto` is to use some set of “clever” booleans in deeply nested ifs or loops. That really doesn't help. Maybe your tools will grok it better, but *you* won't and you're more important. – Donal Fellows May 22 '10 at 22:34
0

I think that the question here is fallacious with respect to the given code.

Consider:

  1. do_something(), init_stuff() and prepare_stuff() appear to know if they have failed, since they return either false or nil in that case.
  2. Responsibility for setting up state appears to be the responsibility of those functions, since there is no state being set up directly in foo().

Therefore: do_something(), init_stuff() and prepare_stuff() should be doing their own cleanup. Having a separate cleanup_1() function that cleans up after do_something() breaks the philosophy of encapsulation. It's bad design.

If they did their own cleanup, then foo() becomes quite simple.

On the other hand. If foo() actually created its own state that needed to be torn down, then goto would be appropriate.

Simon Woodside
  • 7,175
  • 5
  • 50
  • 66
0

Here's what I've preferred:

bool do_something(void **ptr1, void **ptr2)
{
    if (!ptr1 || !ptr2) {
        err("Missing arguments");
        return false;
    }
    bool ret = false;

    //Pointers must be initialized as NULL
    void *some_pointer = NULL, *another_pointer = NULL;

    if (allocate_some_stuff(&some_pointer) != STUFF_OK) {
        err("allocate_some_stuff step1 failed, abort");
        goto out;
    }
    if (allocate_some_stuff(&another_pointer) != STUFF_OK) {
        err("allocate_some_stuff step 2 failed, abort");
        goto out;
    }

    void *some_temporary_malloc = malloc(1000);

    //Do something with the data here
    info("do_something OK");

    ret = true;

    // Assign outputs only on success so we don't end up with
    // dangling pointers
    *ptr1 = some_pointer;
    *ptr2 = another_pointer;
out:
    if (!ret) {
        //We are returning an error, clean up everything
        //deallocate_some_stuff is a NO-OP if pointer is NULL
        deallocate_some_stuff(some_pointer);
        deallocate_some_stuff(another_pointer);
    }
    //this needs to be freed every time
    free(some_temporary_malloc);
    return ret;
}
Mikko Korkalo
  • 375
  • 3
  • 5
0

Old discussion, however.... what about using "arrow anti pattern" and encapsulate later every nested level in a static inline function? The code look clean, it is optimal (when optimisations are enabled), and no goto is used. In short, divide and conquer. Below an example:

static inline int foo_2(int bar)
{
    int return_value = 0;
    if ( prepare_stuff( bar ) ) {
        return_value = do_the_thing( bar );
    }
    cleanup_3();
    return return_value;
}

static inline int foo_1(int bar)
{
    int return_value = 0;
    if ( init_stuff( bar ) ) {
        return_value = foo_2(bar);
    }
    cleanup_2();
    return return_value;
}

int foo(int bar)
{
    int return_value = 0;
    if (do_something(bar)) {
        return_value = foo_1(bar);
    }
    cleanup_1();
    return return_value;
}

In terms of space, we are creating three times the variable in the stack, which is not good, but this disappear when compiling with -O2 removing the variable from the stack and using a register in this simple example. What I got from the above block with gcc -S -O2 test.c was the below:

    .section    __TEXT,__text,regular,pure_instructions
    .macosx_version_min 10, 13
    .globl  _foo                    ## -- Begin function foo
    .p2align    4, 0x90
_foo:                                   ## @foo
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    pushq   %r14
    pushq   %rbx
    .cfi_offset %rbx, -32
    .cfi_offset %r14, -24
    movl    %edi, %ebx
    xorl    %r14d, %r14d
    xorl    %eax, %eax
    callq   _do_something
    testl   %eax, %eax
    je  LBB0_6
## %bb.1:
    xorl    %r14d, %r14d
    xorl    %eax, %eax
    movl    %ebx, %edi
    callq   _init_stuff
    testl   %eax, %eax
    je  LBB0_5
## %bb.2:
    xorl    %r14d, %r14d
    xorl    %eax, %eax
    movl    %ebx, %edi
    callq   _prepare_stuff
    testl   %eax, %eax
    je  LBB0_4
## %bb.3:
    xorl    %eax, %eax
    movl    %ebx, %edi
    callq   _do_the_thing
    movl    %eax, %r14d
LBB0_4:
    xorl    %eax, %eax
    callq   _cleanup_3
LBB0_5:
    xorl    %eax, %eax
    callq   _cleanup_2
LBB0_6:
    xorl    %eax, %eax
    callq   _cleanup_1
    movl    %r14d, %eax
    popq    %rbx
    popq    %r14
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function

.subsections_via_symbols
  • There is problem with this approach as, in this scenario, `cleanup_2()` should most probably happen after `cleanup_3()`, since `prepare_stuff()` seems to use resources initialized by `init_stuff()`. – Jorengarenar Aug 31 '22 at 07:28
0

Yes, its valid and the best practice for exceptions in C. All error handling mechanism of any language just jumps from error to handle like goto to label. But consider placing the label after goto in execution flow and in the same scope.

charles
  • 263
  • 5
  • 7
0

Seems to me that cleanup_3 should do its cleanup, then call cleanup_2. Similarly, cleanup_2 should do it's cleanup, then call cleanup_1. It appears that anytime you do cleanup_[n], that cleanup_[n-1] is required, thus it should be the responsibility of the method (so that, for instance, cleanup_3 can never be called without calling cleanup_2 and possibly causing a leak.)

Given that approach, instead of gotos, you would simply call the cleanup routine, then return.

The goto approach isn't wrong or bad, though, it's just worth noting that it's not necessarily the "cleanest" approach (IMHO).

If you are looking for the optimal performance, then I suppose that the goto solution is best. I only expect it to be relevant, however, in a select few, performance critical, applications (e.g., device drivers, embedded devices, etc). Otherwise, it's a micro-optimization that has lower priority than code clarity.

Ryan Emerle
  • 15,461
  • 8
  • 52
  • 69
  • 4
    That won't cut - cleanups are specific to resources, which are allocated in this order only in this routine. In other places, they aren't related, so calling one from the other doesn't make sense. – Eli Bendersky Apr 25 '09 at 14:15
-1

I prefer using technique described in the following example ...

struct lnode *insert(char *data, int len, struct lnode *list) {
    struct lnode *p, *q;
    uint8_t good;
    struct {
            uint8_t alloc_node : 1;
            uint8_t alloc_str : 1;
    } cleanup = { 0, 0 };

    // allocate node.
    p = (struct lnode *)malloc(sizeof(struct lnode));
    good = cleanup.alloc_node = (p != NULL);

    // good? then allocate str
    if (good) {
            p->str = (char *)malloc(sizeof(char)*len);
            good = cleanup.alloc_str = (p->str != NULL);
    }

    // good? copy data
    if(good) {
            memcpy ( p->str, data, len );
    }

    // still good? insert in list
    if(good) {
            if(NULL == list) {
                    p->next = NULL;
                    list = p;
            } else {
                    q = list;
                    while(q->next != NULL && good) {
                            // duplicate found--not good
                            good = (strcmp(q->str,p->str) != 0);
                            q = q->next;
                    }
                    if (good) {
                            p->next = q->next;
                            q->next = p;
                    }
            }
    }

    // not-good? cleanup.
    if(!good) {
            if(cleanup.alloc_str)   free(p->str);
            if(cleanup.alloc_node)  free(p);
    }

    // good? return list or else return NULL
    return (good? list: NULL);

}

source: http://blog.staila.com/?p=114

Nitin Kunal
  • 565
  • 5
  • 7
  • 3
    flaggy code and the arrow anti-pattern (both showcased in your example) are both things that needlessly complicate code. There isn't any justification outside of "goto is evil" to use them. – KingRadical Nov 06 '13 at 10:45
-2

We use Daynix CSteps library as another solution for the "goto problem" in init functions.
See here and here.

TheCodeArtist
  • 21,479
  • 4
  • 69
  • 130