8 ms·
They're avoiding the "not used right" problem in flight control software. The full quote is "Simpler control flow translates into stronger capabilities for bot
by alayne 11y ago
They're avoiding the "not used right" problem in flight control software.
The full quote is "Simpler control flow translates into stronger capabilities for both human and tool-based
analysis and often results in improved code clarity. Mission critical code should not just be arguably, but trivially correct."
So there are reasons beyond code clarity.
Seems reasonable to me.
- AbacusAvenger 11y agoGotos are trivial. They map directly to unconditional jumps at the assembly level. They're one of the best ways to implement exception handling in C (look at how the Linux kernel uses them). It's also not unusual to have conditionals that don't nest. Of course, if you really want to avoid using a goto, you have the option of duplicating a lot of code in order to satisfy all the code paths, decreasing readability and making it harder to maintain. Or you can just use a goto and not worry about it. I do understand why people want to avoid 'goto', but Dijkstra wasn't always right...
- deleted 11y ago[deleted]
- nitrogen 11y agoIf the compiler in question does tail call optimization (or even if not), maybe one could write a cleanup function that takes the place of the goto block, but this looks pretty ugly with all the parameters: int cleanup(void *ptr1, char *ptr2, struct foo *ptr3, int *ptr4, union xyz *ptr5, int ret) { free(ptr1); // etc. return ret; } // later... if(fail1) { return cleanup(a, b, c, d, e, -1); }
- hyc_symas 11y agoI was at JPL when we were developing these standards. C was still pretty new there, they used FORTRAN almost exclusively before that. In practice, this rule had little impact. The only time you could justify using a goto was for cleanup, and you could always end-run around the rule. ... blah blah ... if (error) goto fail; ... blah blah ... if (error) goto fail; ... blah blah ... fail: ... cleanup ... just gets turned into do { ... blah blah ... if (error) break; ... blah blah ... if (error) break; ... etc... } while (0); if (error) { ... cleanup ... }
- marcoperaza 11y agoThe 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.