Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!seismo!umcp-cs!chris From: chris@umcp-cs.UUCP (Chris Torek) Newsgroups: net.lang.c Subject: Re: C-STYLE (goto?) Message-ID: <3200@umcp-cs.UUCP> Date: Sat, 30-Aug-86 08:47:46 EDT Article-I.D.: umcp-cs.3200 Posted: Sat Aug 30 08:47:46 1986 Date-Received: Sat, 30-Aug-86 19:59:05 EDT References: <3253@brl-smoke.ARPA> <264@killer.UUCP> Reply-To: chris@umcp-cs.UUCP (Chris Torek) Organization: University of Maryland, Dept. of Computer Sci. Lines: 80 >In article <3253@brl-smoke.ARPA> jeff@isi-vaxa.ARPA writes: >> setup_step_1; >> if ( (status = step_1()) == FAILURE ) >> goto abort_operation; ... >> setup_step_n; >> if ( (status = step_n()) == FAILURE ) >> goto abort_operation; >> >>abort_operation: >> >> cleanup(); >> return (status); >>Now, I know a lot of people detest this because of the use of goto's .... In article <264@killer.UUCP> toma@killer.UUCP (Tom Armistead) replies: >There is always the use of setjmp and longjmp to acomplish this task .... [example deleted] >This will accomplish the same thing and possibly not affend the goto haters, >although it is somewhat harder to follow if you are no farmiliar with >setjmp() and lonjmp(). Alas, longjmp() is nothing more than goto in disguise. Indeed, this is a far more powerful version of goto than the `plain C' goto, and should by rights be correspondingly more offensive to goto haters. In general, when choosing between two existing primitives, if one is less powerful but does what you need, use that. When designing primitives, the choice is more difficult: something that does less is easier to understand, but you may need too many of those things, making the whole harder to comprehend. As to a specific solution to the original problem above, I think there is no `best' answer, at least not without more information. I happen to like the pointer table method proposed by grt@twitch; it can be expanded in a useful way for recovery and/or error explanation: int dostep1(), dostep2(), dostep3(), dostep4(); int fix1or3(), fix2(); struct step { char *s_name; /* name of this step */ int (*s_func)(); /* function that implements it */ int (*s_fixup)(); /* repair after error */ } steps[] = { { "step1", dostep1, fix1or3 }, { "step2", dostep2, fix2 }, { "step3", dostep3, fix1or3 }, { "step4", dostep4, 0 }, 0 /* marks end of table */ }; f() { register struct step *s; int status; for (s = steps; s->s_func != NULL; s++) { if ((status = (*s->s_func)()) != SUCCESS) { if (s->s_fixup == NULL || (*s->s_fixup)(status) != SUCCESS) { fprintf(stderr, "\ Unrecoverable failure in %s, code %d\n", s->s_name, status; break; } } } return (status); } There may be cases, however, where a direct-coded state machine with a `switch' statement would be better; and there may be cases where the original code runs fastest and/or is clearest (whichever is more important to the coder/maintainer and user). -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1516) UUCP: seismo!umcp-cs!chris CSNet: chris@umcp-cs ARPA: chris@mimsy.umd.edu