Live data from Hacker News

Error handling style in C

pixelstech.net

11–20 of 45 posts

Re: Error handling style in C

#11

the goto style. you can escape goto by the following trick: do { if (!do_something( bar )) { break; } if (!init_stuff( bar )) { break; } if (!prepare_stuff( bar )) { break; } return do_the_thing( bar ); }while(0); return 0;

The “goto” style is better; it avoids excessive nesting and abusing “do { … } while (…)” for something wholly unrelated to looping. You want a jump, use a jump—it’s okay. “goto” lets you unwind from any number of errors without undue nesting or repetition:

    int foo(int bar) {

        if (!do_something(bar))
            goto do_something_error;

        if (!init_stuff(bar))
            goto init_stuff_error;

        if (!prepare_stuff(bar))
            goto prepare_stuff_error;

        return do_the_thing(bar);

    prepare_stuff_error:
        unprepare_stuff(bar);

    init_stuff_error:
        deinit_stuff(bar);

    do_something_error:
        undo_something(bar);

        return 0;

    }

Re: Error handling style in C

#12
I think that this[1] email thread between Torvalds and various other kernel developers sums up the use of goto in C the best.

I find that those who think that any given programming concept is "inherently" bad to be dangerous, especially if they were just taught that way.

[1] http://kerneltrap.org/node/553/2131

Re: Error handling style in C

#13

the goto style. you can escape goto by the following trick: do { if (!do_something( bar )) { break; } if (!init_stuff( bar )) { break; } if (!prepare_stuff( bar )) { break; } return do_the_thing( bar ); }while(0); return 0;

The “goto” style is better; it avoids excessive nesting and abusing “do { … } while (…)” for something wholly unrelated to looping. You want a jump, use a jump—it’s okay. “goto” lets you unwind from any number of errors without undue nesting or repetition: int foo(int bar) { if (!do_something(bar)) goto do_something_error; if (!init_stuff(bar)) goto init_stuff_error; if (!prepare_stuff(bar)) goto prepare_stuff_error;…

Yup, jumps are the best way to correctly unwrap things (e.g. if you acquired resources), and if you're using it when you need to you might as well use it everywhere so your codebase is consistent.

Although I believe your jumps are in the wrong place logically speaking: if do_something() errored out, logically speaking you should not have to undo_something because do_something cleaned up its crap. So the jumps should be after the cleanups (and you should never need to unprepare_stuff).

Just as the opening of a file is not in a protected scope (if opening the file fails, you don't close it)

Re: Error handling style in C

#14
post #10
post #5

Why goto is bad? Again this "structured programming" bullshit. Kernel uses 2 a lot to handle errors like here [1]. [1] https://github.com/torvalds/linux/blob/master/mm/shmem.c#L99...

In general, gotos are bad. For error handling in C, they're perfect. Just don't jump backwards in code.

Jumping backwards is fine, too. It's okay to ignore Dijkstra when he is wrong.

Re: Error handling style in C

#15

Earlier quoted context omitted.

The “goto” style is better; it avoids excessive nesting and abusing “do { … } while (…)” for something wholly unrelated to looping. You want a jump, use a jump—it’s okay. “goto” lets you unwind from any number of errors without undue nesting or repetition: int foo(int bar) { if (!do_something(bar)) goto do_something_error; if (!init_stuff(bar)) goto init_stuff_error; if (!prepare_stuff(bar)) goto prepare_stuff_error;…

Yup, jumps are the best way to correctly unwrap things (e.g. if you acquired resources), and if you're using it when you need to you might as well use it everywhere so your codebase is consistent. Although I believe your jumps are in the wrong place logically speaking: if do_something() errored out, logically speaking you should not have to undo_something because do_something cleaned up its crap. So the jumps should…

You’re right, you shouldn’t need to clean up after the first failure. I was thinking, say, new_a() succeeds and new_b() fails: you don’t need to free_b() but you definitely need to free_a(). The calling function is responsible for coordinating error handling amongst those functions it calls.

“…you should never need to unprepare_stuff”

I was just poking fun at the useless names.

Re: Error handling style in C

#16
post #9

A forward jumping goto (to a single target inside a function) is just perfect for C error handling code. Don't be misguided by a silly principle of goto's being always bad. They get the job done in the cleanest possible way, so you should use them for doing cleanups. The examples did not have any resources to clean up, and that is what makes error handling in C painful. In the absence of any cleanup routines, this wi…

Why should the goto be to one single target? Multiple goto statements are good for multiple clean ups without adding indentation levels and without having artificially long logic ands. For example:

    int init_abc()
    {
        if (!init_a())
            goto err_a;
        if (!init_b())
            goto err_b;
        if (!init_c())
            goto err_c;
        return 1;

     err_c:
        cleanup_b();
     err_b:
        cleanup_a();
     err_a:
        return 0;
    }
seems to be the cleanest way to do what it does in C. For what it's worth, it is the way a lot of error handling is done in the Linux kernel.

Re: Error handling style in C

#17
post #6

There's the 5th method that I tend to use: if (!init_stuff(bar)) { return FALSE; } if (!do_the_thing(bar)) { return FALSE; } return TRUE;

I do the same thing with my code, but I think it clutters it up and it's hard to follow the logic this way with clean up code all over the place.

Re: Error handling style in C

#18
post #9

A forward jumping goto (to a single target inside a function) is just perfect for C error handling code. Don't be misguided by a silly principle of goto's being always bad. They get the job done in the cleanest possible way, so you should use them for doing cleanups. The examples did not have any resources to clean up, and that is what makes error handling in C painful. In the absence of any cleanup routines, this wi…

Why should the goto be to one single target? Multiple goto statements are good for multiple clean ups without adding indentation levels and without having artificially long logic ands. For example: int init_abc() { if (!init_a()) goto err_a; if (!init_b()) goto err_b; if (!init_c()) goto err_c; return 1; err_c: cleanup_b(); err_b: cleanup_a(); err_a: return 0; } seems to be the cleanest way to do what it does in C. F…

I guess it's fine to use multiple targets too. However, usually you can get away with one, because free(NULL) and similar cleanups tend to be no-ops. So you have something like:

    char *foo = 0, *bar = 0;
    if((foo = malloc(X)) == NULL || (bar = malloc(Y)) == NULL)
      goto cleanup;
    make_me_millions(foo, bar);

  cleanup:
    free(bar);
    free(foo);
In this case, and many cases like it, there's no need to have two jump targets, because one is good enough. You'll have to declare the variables early on anyway to avoid warnings/errors from definitions that cross jump labels.

So there's probably nothing wrong with multiple jump targets but that might not be needed with well-behaving cleanup functions.

Re: Error handling style in C

#19
post #14
post #10

Earlier quoted context omitted.

In general, gotos are bad. For error handling in C, they're perfect. Just don't jump backwards in code.

Jumping backwards is fine, too. It's okay to ignore Dijkstra when he is wrong.

How dare you imply that programming could possibly be subtle or nuanced or require thought? The very idea that our entire field of expertise could not be adequately conveyed by an eight-page Microsoft Word document of absolute endorsements and prohibitions—laughable!

Re: Error handling style in C

#20
post #9

A forward jumping goto (to a single target inside a function) is just perfect for C error handling code. Don't be misguided by a silly principle of goto's being always bad. They get the job done in the cleanest possible way, so you should use them for doing cleanups. The examples did not have any resources to clean up, and that is what makes error handling in C painful. In the absence of any cleanup routines, this wi…

Why should the goto be to one single target? Multiple goto statements are good for multiple clean ups without adding indentation levels and without having artificially long logic ands. For example: int init_abc() { if (!init_a()) goto err_a; if (!init_b()) goto err_b; if (!init_c()) goto err_c; return 1; err_c: cleanup_b(); err_b: cleanup_a(); err_a: return 0; } seems to be the cleanest way to do what it does in C. F…

Good points - it also a lot easier to debug what's being returned by the function as you have fewer breakpoints to set in gdb.
Post reply on HN