4 ms·
The advantage of gotos is clear next to the alternatives. First, the dreadful pyramid style: err = funcA(&resourceA) if (succeeded(err)) { err = fun
by marcoperaza 11y ago
The advantage of gotos is clear next to the alternatives. First, the dreadful pyramid style:
err = funcA(&resourceA)
if (succeeded(err)) {
err = funcB(&resourceB)
if (succeeded(err)) {
err = funcC(&resourceC)
if (succeeded(err)) {
...and so forth...
cleanup(resourceC)
}
free(resourceB)
}
specialFree(resourceA);
}
return err;
A new indentation level for every single function that can fail or return a resource. Hard to read and hard to edit.
Even worse is duplicating the cleanup code:
err = funA(&resourceA)
if (failed(err)) {
specialFree(resourceA);
return err;
}
err = funB(&resourceB)
if (failed(err)) {
free(resourceB);
specialFree(resourceA);
return err;
}
err = funC(&resourceC)
if (failed(err)) {
cleanup(resourceC);
free(resourceB);
specialFree(resourceA);
return err;
}
...and so forth...
Now with gotos:
err = funA(&resourceA);
if (failed(err))
goto Error;
err = funB(&resourceB);
if (failed(err))
goto Error;
err = funC(&resourceC);
if (failed(err))
goto Error;
Error:
if (resourceA)
specialFree(resourceA);
if (resourceB)
free(resourceB);
if (resourceC)
cleanup(resourceB);
free err;
Much better. If you use the same error code type across the project, define a macro for "if (failed(err)) goto Error;" to save the keystrokes and lines-of-code. Some projects prefer to use a different goto label for each exit point, somewhat in lieu of ifs. The advantage of using ifs is that you can usually be loose about the order of resource cleanup.
This use of goto is widespread and highly structured, so tools should be able to handle it too.
- deleted 11y ago[deleted]
- keenerd 11y agoStill seems like a needless use of goto. struct resources { resourceA *a; resourceB *b; resourceC *c; }; int init(resources *r) { err = funA(r->a); if (err) {cleanup(r); return err;} err = funB(r->b); if (err) {cleanup(r); return err;} err = funC(r->c); if (err) {cleanup(r); return err;} return 0; } void cleanup(resources *r) { if (r->a) {specialFree(r->a);} if (r->b) {specialFree(r->b);} if (r->c) {specialFree(r->c);} free r; } And this has the added benefit of being more structured, more testable. The same cleanup() code can be used for normal cleanup as well as error handling, so you have less duplication.
- marcoperaza 11y agoThis doesn't have any advantages over the goto version. So now you're defining a struct and a special cleanup function for every function that you write. And in the name of what? A religious avoidance of goto?
- Gibbon1 11y ago> A religious avoidance of goto? That's my experience. In school the no goto rule is exactly like your second grade teacher telling you never to start a sentence with the word 'and' It's to prevent this; And we went to the park. And I played on the swing And then we went and got ice cream. And we went home and had dinner. The no goto rule is for novices to teach them not to write spaghetti code.
- deleted 11y ago[deleted]