void function(void)
{
int foundIt;
while(<condition1>)
{
foundIt = 0;
while(<condition2>)
{
if(<condition3>)
{
foundIt = 1;
break;
}
}
if(foundIt)
{
continue;
}
...
}
}
void function2(void)
while(<condition1>)
{
while(<condition2>)
{
if(<condition3>)
{
goto continueouter;
}
}
goto skipcontinue;
continueouter:
continue;
skipcontinue:
...
}
}
I wouldn't consider either a huge problem but I prefer the former to
the latter. But in the latter your gotos are all going in a forward
direction so it's ok. 'goto' only really becomes a problem if it's
overused [more so you have many labels] and/or you jump backwards in
the code.
I use goto for error cleanup for instance where they all are directed
to the tail end of the function to free up resources/etc. the nested
if e.g.
if (op() == OK) {
if (op() == OK) {
if (op() == OK) {
...
Is fucking ugly and I think should be destroyed on sight.
Tom
Actually, Now that I think about it, I guess there is a third option.
Not sure which I prefer now. This one does have one less goto, but,
then again, the label could be far from the goto if the while block is
long, so it might be hard to see where the goto goes to.
void function2(void)
while(<condition1>)
{
while(<condition2>)
{
if(<condition3>)
{
goto continueouter;
}
}
...
continueouter:
;
}
}
I think you removed too much content because this could be rewritten to:
while(<condition1>)
{
int foundIt = 0;
while(!foundIt && <condition2>)
if(<condition3>)
foundIt = 1;
if (!foundIt) {
...
}
}
Which looks very natural.
) void function2(void)
) while(<condition1>)
) {
) while(<condition2>)
) {
) if(<condition3>)
) {
) goto continueouter;
) }
) }
) goto skipcontinue;
) continueouter:
) continue;
) skipcontinue:
) ...
) }
) }
Same here:
while(<condition1>) {
while(<condition2>)
if(<condition3)
goto skip:
...
skip:
}
But if you delegate the while(<condition2>) if(<condition3>) loop to a
function you get this:
while(<condition1>) {
if(look_for_condition3_in_condition2(...)) {
...
}
}
Which is a clear winner.
Details depend on the conditions, of course. Got a real-world example ?
SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
> Actually, Now that I think about it, I guess there is a third option.
> Not sure which I prefer now. This one does have one less goto, but,
> then again, the label could be far from the goto if the while block is
> long, so it might be hard to see where the goto goes to.
That's what comments are for ...
/* jump to end of outer loop */ and
/* jump label from inner loop */
Rgds
Denis McMahon
It would be difficult to post the actual example since it's kind of
involved. Basically, I'm trying to solve the "Strawberry Fields"
problem at:
http://www.itasoftware.com/careers/work-at-ita/hiring-puzzles.html
and part of my algorithm has a nested condition that needs to break
out of an outer loop. I'll probably be refactoring once I get the
algorithm to be sufficiently fast, so the problem may be moot.
However, considering that I may decide to send this in to them to try
to get hired, I do want this code to be reasonably stylistically
standard.
--
Dan Giaimo
I second that. "Keep the code to the left" is a good policy and if a
goto can help with it (without causing any confusion, of course, so it
has to go forward, use clearly named labels, etc), it just adds to the
value of structural programming!
Regards,
Felix
--
Felix Palmen (Zirias) + [PGP] Felix Palmen <fe...@palmen-it.de>
web: http://palmen-it.de/ | http://palmen-it.de/pub.txt
my open source projects: | Fingerprint: ED9B 62D0 BE39 32F9 2488
http://palmen-it.de/?pg=pro + 5D0C 8177 9D80 5ECF F683
The most structured way is using neither break/continue nor goto:
void function(void)
{
while (condition1) {
while (condition2 && ! condition3) {
...
}
if (! condition3) {
...
}
}
}
It's much easier to reason about the correctness of a while loop if the
loop condition alone will determine when the loop is done.
/August
I think you mean:
void function(void)
{
while (condition1 && ! condition3) {
while (condition2 && ! condition3) {
...
}
if (! condition3) {
...
}
}
}
Right?
--
Dan Giaimo
Total nonsense. Convoluted loop conditions blur the meaning. break and
goto significantly simplify such constructs.
--
"Avoid hyperbole at all costs, its the most destructive argument on
the planet" - Mark McIntyre in comp.lang.c
The best way to keep the code to the left and keep the logic clear is to
move the inner loops out to their own functions. That way "return" is
all that's required to break out.
--
Ian Collins
Talking about loops, as soon as the inner code has a reasonable meaning
by itself, yes. But the example I quoted here (common cleanup code) is a
typical good usage of goto making the code even more readable.
There's never a good use for goto!
--
Ian Collins
Dogmatic "opinions" are just stupid.
> I think you mean:
> while (condition1 && ! condition3) {
> while (condition2 && ! condition3) {
I'm not going to try to figure out whether dgaimo or August
is correct here -- can you post a version with *goto* so it's
easier to see the logic :-).
I *will* say this is NOT the first time c.l.c posters have
"improved" code by replacing goto with "structure" and ended up
*reversing* the test! Goto is much more straightforward:
you figure out where you want to go, and you (guess what?)
GO there.
Since goto is so easy for humans to grasp, one wonders if
the antidisestablishmentarians are concerned about automated
logic verifiers. Uhh, IMO by the time those are advanced
enough to be useful, they won't be confused by goto's.
c.l.c offers one-stop shopping for opinions about C style:
On Sep 15, 12:48 pm, Ian Collins <ian-n...@hotmail.com> wrote:
> There's never a good use for goto!
On Sep 15, 11:40 am, Richard <rgrd...@gmail.com> wrote:
> Total nonsense. Convoluted loop conditions blur the meaning. break and
> goto significantly simplify such constructs.
> ...
> "Avoid hyperbole at all costs, its the most destructive argument on
> the planet" - Mark McIntyre in comp.lang.c
Read a .sig line for a hint as to who is correct here. :-)
On Sep 15, 2:43 am, dgiaimo <dgia...@gmail.com> wrote:
> However, considering that I may decide to send this in to them to try
> to get hired, I do want this code to be reasonably stylistically
> standard.
Then the question is easy to answer. Those who accept goto
tend not to be dogmatic and the silly
if (redundant_predicate) break;
won't bother them. The converse is NOT true: 'goto'
*might* very well be just the thing that blocks your hiring.
James Dow Allen
'goto' can be enjoyable. Note how 'free(resource2)' might be
unreachable until 'USE_BAZ' is #defined as non-zero, but note the
consistency and legibility.
-----
#include <stdlib.h>
#include <stdio.h>
#define USE_BAZ 0
struct foo {
int x;
};
struct bar {
long x;
};
struct baz {
double x;
};
static const char *const test(void) {
const char *ret = "Failure.";
struct foo *resource1;
struct bar *resource2;
#if USE_BAZ
struct baz *resource3;
#endif
/**
* Resource allocation section.
**/
resource1 = malloc(sizeof resource1);
if (!resource1) {
fprintf(stderr, "'malloc' failed for 'resource1'.\n");
goto err_resource1;
}
resource2 = malloc(sizeof resource2);
if (!resource2) {
fprintf(stderr, "'malloc' failed for 'resource2'.\n");
goto err_resource2;
}
#if USE_BAZ
resource3 = malloc(sizeof resource3);
if (!resource3) {
fprintf(stderr, "'malloc' failed for 'resource3'.\n");
goto err_resource3;
}
#endif
/**
* Processing section.
**/
/* ... */
ret = "Success.";
/**
* Cleanup section.
**/
#if USE_BAZ
free(resource3);
err_resource3:
#endif
free(resource2);
err_resource2:
free(resource1);
err_resource1:
return ret;
}
int main(void) {
puts(test());
return 0;
}
Only if you miswrote your original code, which, as-is, repeated the outer
loop when condition3 was found, with a 'continue'. What you wrote here
breaks the outer loop instead.
Scratch the unreachable bit. That only counts if we leave resources
intact and:
return "Success.";
rather than:
ret = "Success.";
(Oops. Started the example one way then changed my mind during.)
Well there may be one, but I've haven't found it in my 30 odd years of C
programming. I'll let you if I do find one.
--
Ian Collins
I'd use
while(condition1)
{
while(condition2)
{
if(condition3)
break;
...
}
if(condition2)
continue;
...
}
Obviously condition2 needs to be a simple variable, not a call.
The single best example, common cleanup code at the end of a function,
was already given in this thread. If during all your years of
programming, you just had Dijkstra's famous letter in your mind, it may
of course never have occured to you that a goto in such kind of code
would /improve/ the structure.
A lot of people tend to forget about the horrible ancient code that lead
to this goto-animosity in the first place.
> * Ian Collins <ian-...@hotmail.com>:
>> On 09/15/10 06:36 PM, Felix Palmen wrote:
>>> * Ian Collins<ian-...@hotmail.com>:
>>>> There's never a good use for goto!
>>>
>>> Dogmatic "opinions" are just stupid.
>>
>> Well there may be one, but I've haven't found it in my 30 odd years of C
>> programming. I'll let you if I do find one.
>
> The single best example, common cleanup code at the end of a function,
> was already given in this thread. If during all your years of
> programming, you just had Dijkstra's famous letter in your mind, it may
> of course never have occured to you that a goto in such kind of code
> would /improve/ the structure.
There's probably not much merit in adding my own preferences to this
thread, but I tend to use the "huge tree of nested if's" Tom St Denis
seems to hate so much (IIRC -- sorry if I'm wrong, Tom!) If the tree grows
too deep, I break it up into functions. I use goto only under exceptional
conditions, where I perceive it to provide a big jump in expressive power.
Example:
----v----
From: "Ersek, Laszlo" <la...@caesar.elte.hu>
Newsgroups: comp.unix.programmer
Subject: Re: verifying file ownership with dlopen()
Date: Wed, 5 May 2010 19:01:46 +0200
Message-ID: <Pine.LNX.4.64.10...@login01.caesar.elte.hu>
On Wed, 5 May 2010, Nicolas George wrote:
> David Schwartz wrote in message
> <f37d8057-9e6b-4e9a...@k25g2000prh.googlegroups.com>:
>> I think it's likely a better idea to check the ownership and
>> permissions of the directory the library is in before calling 'dlopen'.
>
> He needs to check every path element that leads to the library, because
> either one could be a symlink.
>
> And the check must be aware of the local system's rules for permissions.
> For example, just checking the file mode may not be enough if there are
> ACLs.
Once the full pathname has been tokenized, the way to do that might be
something like this. (Note - I didn't even try to compile this. It's here
only to invite criticism and/or better ideas.)
int
safeopen(const char * const * elem, size_t nelem)
{
if (0u < nelem) {
int fd;
size_t cur;
fd = open("/", O_SEARCH | O_DIRECTORY | O_NOFOLLOW);
cur = 0u;
loop:
if (-1 != fd) {
struct stat s;
if (0 == fstat(fd, &s)) {
if (cur == nelem) {
if (S_ISREG(s.st_mode) && /* other checks on the executable */) {
return fd;
}
}
else {
if (/* checks on the directory */) {
int tmp;
tmp = fd;
fd = openat(fd, elem[cur], O_NOFOLLOW
| (cur < nelem - 1u ? O_SEARCH | O_DIRECTORY : O_EXEC));
if (0 == close(tmp)) {
++cur;
goto loop;
}
}
}
}
if (-1 != fd) {
(void)close(fd);
}
}
}
return -1;
}
The returned int (if not -1) could be used with fexcve(). Unfortunately
(or not), there is no "dlfdopen()".
The code could hang indefinitely if the last component identified a FIFO.
I'd OR O_NONBLOCK with O_EXEC, if not for O_NONBLOCK being unspecified for
regular files. (I can't see anything in the openat() spec that would
require O_EXEC to fail on a FIFO (and immediately).)
[...]
----^----
Furthermore, nested if's (just like judiciously placed labels) are very
suitable for "cascading release of resources". IIRC, you said something to
the effect of "code should be condensed on the left". I sort of disagree.
Staying with the case where the if-tree still fits reasonably in a
function, I like an indentation that reflects the dependency depth of the
dependent code. (I use a "two spaces" indentation style, obviously.) With
goto's, the dependency depth is the same, but it doesn't show.
A random link:
http://en.wikipedia.org/wiki/Cyclomatic_complexity
goto's probably don't change the control flow graph, just tend to distance
the source's appearance from the CFG.
Nested if's also allow block folding in many editors and "find matching
brace".
lacos
There are a lot of typical tasks in a C program that need to invoke some
functions, checking errors on each of them. Take for example some code
using BSD sockets. Splitting these tasks doesn't make sense.
Now, if you write it like this:
if (do_foo() >= 0)
{
if (do_bar() >= 0)
{
if (do_baz() >= 0)
{
...
It'll get unreadable very soon. The much better style, even representing
the sequential nature of the code optically, is this:
if (do_foo() < 0)
{
return -1;
}
if (do_bar() < 0)
{
return -1;
}
...
do_real_work()
...
And, in case cleanup code is needed, replace the return statements with
gotos.
> goto's probably don't change the control flow graph, just tend to distance
> the source's appearance from the CFG.
I'd argue that you ultimately want readable (as in: human-readable)
code. So, I consider it much more important to reflect the semantics of
the code in it's structure, rather than the control flow graph.
In most XML-based languages (consider e.g. BPEL, XLANG, ...), you are
very much bound to the control flow. This results in the typical
application of such languages, to describe highly sequential processes.
Anything else would result in ridiculously deep nesting.
Regards,
Felix
> if(foundIt)
> {
> continue;
> }
You seem to like braces and white space (except where it would be useful).
What's wrong with:
if (foundIt) continue; /* ? */
> if(<condition3>)
> {
> goto continueouter;
> }
> }
> goto skipcontinue;
> continueouter:
> continue;
> skipcontinue:
> ...
As written, this second form is more confusing; most of the these lines seem
to contain 'continue' in some form. This would be better as:
if (foundIt) goto nextouter;
....
nextouter:
}
as I think you've posted elsewhere.
Either form is OK I think. Certainly for getting code to work, use whatever
you like. For code to be read by someone else (or perhaps ported to a
language without gotos), I'd think twice about using goto.
(This problem would be very easily solved by a bit of extra syntax (continue
n or continue outer for example). But no-one seems to like adding extra
syntax to a language even though it is fairly trivial to implement (eg. ten
lines of code in one of my projects). It's far better of course for hundreds
of thousands of programmers to each add hundreds of lines of convoluted code
instead.)
--
Bartc
> ........
This got posted unintentionally. Please ignore.
--
Bartc
Really what you are saying here is that error conditions obscure the
normal flow of the code.
try .. throw ... catch
is the C++ and Java answer, which brings its own problems.
At least, that's a common aspect.
> try .. throw ... catch
>
> is the C++ and Java answer, which brings its own problems.
You could also argue that a throw - catch inside one method is (in terms
of control flow) equivalent to a goto <error-label>, while a catch
somewhere up the caller-chain would be a global goto, a longjmp in C.
I use the if(err) goto error_exit; construct a lot, however.
> I was wondering what style of programming is preferred when continuing
> an outer loop from within an inner loop. As far as I see it, there
> are two possibilities, which I have given in pseudo-code below. The
> first follows the strict, structural programming rule of only using
> breaks and continues to control flow, while the second uses gotos.
> Personally I prefer the second since it seems clearer, seems like it
> would be easier to optimize, and doesn't use an artificial control
> variable. What do you think?
I too prefer gotos in such a case. If you're disciplined and know what
you're doing there's nothing wrong with it.
You post brought me to an idea, something that may be added to the
next C version: Labeled loops. I'm thinking of something like this
(since this would be an extension to C99, I'm using C99 features down
there (variable declaration in for clause, mixed code and declaration).
Also you'd like to know if you broke from a inner loop. For that I
suggest a virtual label break, that's used like switch, where the case
refers to the label of the inner loop, default case if the break
happend on outermost loop level. As a shorthand just break: without
cases if you're only interested on an outmost loop break. Following
such a break:-scope with else, the else-scope is executed if no break
happened.
/* ... */
int j_from_i(int i);
#define MAX_J 1000
extern int N;
void foo(void)
{
for : outer (int i=0; i<N; i++) {
int j = j_from_i(i);
while : inner (j) {
if(j > MAX_J)
break outer;
for(int k=j; k>0; k--) {
/* ... */
if(k == i)
break inner;
}
}
} break: {
case inner:
break;
default:
}
}
/* ... */
Another thing that would be valueable would deinitializers, e.g.
putting the commands perform (in opposite order) whenever a scope is
left, after the given deinitialzer has been reached. To avoid adding a
new keyword I suggest using return as a (virtual) scope-label, i.e. a
scope following immetiately after return would be delayed until the
scope is left. Since return is a keyword, using it like a label would be
unambigous.
return-scopes are executed in opposite order of definition. Also
within a return-scope using control flow statements that'd leave the
return-scope would be illegal.
#include <stdio.h>
struct spam;
struct spam * new_spam(void);
void free_spam(struct spam * s);
struct eggs;
struct eggs * new_eggs(void);
void free_eggs(struct eggs * e);
struct spam_n_eggs;
struct spam_n_eggs * new_spam_n_eggs(struct spam * s, struct eggs * e);
void free_spam_n_eggs(struct spam_n_eggs * se);
void spam_n_eggs_process(struct spam_n_eggs * se, int i);
extern int debug;
void bar(void)
{
struct spam * s = newspam();
return: {
free_spam(s);
if(debug)
fprintf(stderr, "DEBUG: spam at %p freed.\n",
s);
}
if(s) {
if(debug)
fprintf(stderr, "DEBUG: new spam at %p.\n", s);
} else {
if(debug)
fprintf(stderr, "DEBUG: allocating spam
failed.\n");
return;
}
struct eggs * e =
return: {
free_eggs(e);
if(debug)
fprintf(stderr, "DEBUG: eggs at %p freed.\n",e);
}
if(e) {
if(debug)
fprintf(stderr, "DEBUG: new eggs at %p.\n", e);
} else {
if(debug)
fprintf(stderr, "DEBUG: allocating eggs
failed.\n");
return;
}
if(debug)
fprintf(stderr, "DEBUG: processing spam and
eggs.\nDEBUG: ");
for(i=0; i<10; i++) {
struct spam_n_eggs * se = new_spam_n_eggs(s, e);
return: free_spam_n_eggs(se);
if(!se)
break;
spam_n_eggs_process(se);
if(debug) {
fprintf(stderr, "%d... ", %i);
fflush(stderr);
}
} break: {
if(debug)
fprintf(stderr, "error.\n");
} else if(debug)
fprintf(stderr, "done.\n");
}
Running this program in debug mode would print:
DEBUG: new spam at 0xabcd0123.
DEBUG: new eggs at 0xcdef2345.
DEBUG: processing spam and eggs.
DEBUG: 0... 1... 2... 3... 4... 5... 6... 7... 8... 9... done.
DEBUG: eggs at 0xcdef2345 freed.
DEBUG: spam at 0xabcd0123 freed.
If allocating a spam_n_eggs fails:
DEBUG: new spam at 0xabcd0123.
DEBUG: new eggs at 0xcdef2345.
DEBUG: processing spam and eggs.
DEBUG: 0... 1... 2... 3... error.
DEBUG: eggs at 0xcdef2345 freed.
DEBUG: spam at 0xabcd0123 freed.
If allocating the eggs fails:
DEBUG: new spam at 0xabcd0123.
DEBUG: allocating eggs failed.
DEBUG: spam at 0xabcd0123 freed.
Having such a scope finalizing facility would eleminiate 99% of the
cases were today goto is used in a usefull way.
Just a few ideas. Comments?
Wolfgang
--
CIP-Rechnerbetriebsgruppe
Fakultät für Physik - LMU München
Schellingstrasse 4, 80799 München
Absolute statements are always wrong.
--
Larry Jones
I don't NEED to compromise my principles, because they don't have
the slightest bearing on what happens to me anyway. -- Calvin
Pulling the "year" card is not a good idea. It generally means you're a
stuck in the mud curmudgeon who hasnt moved with the times and likes to
brow beat people with years served rather than relevant advice. Industry
is full of them.
Hint : Bill has been programming C for years too ;)
(No, I do know you know what you're posting about most of the time..).
Some languages with exceptions also provide most if not all of the
solutions to those problems. If you have exceptions, you have to be
able to write exception safe code. To write exception safe code, you
have to be able to automate clean up, so you can use early return safely.
--
Ian Collins
That wasn't the point I was making. I was replying to your post (sorry
I replied to the wrong message):
"try .. throw ... catch
is the C++ and Java answer, which brings its own problems."
Languages with exceptions also provide a mechanism to clean up when an
exception is thrown, either through automatic object destruction (C++)
or finalisers (Java, PHP and others).
--
Ian Collins
How about:
if (op() != OK) {}
else if (op() != OK) {}
else if (op() != OK) {}
...
Or as I prefer to write it:
if (op() == FAIL)
{
/* first op failed */
}
else if (op() == FAIL)
{
/* second op failed */
}
else if (op() == FAIL)
{
/* third op failed */
}
else
{
/* success */
}
I'm very predjudiced against goto, and dislike excessive nesting.
Paul.
That's one possible style I'd recommend (and probably Tom would, too).
But ...
> I'm very predjudiced against goto, and dislike excessive nesting.
Where exactly do you put the cleanup code in this case? Without using
goto, you'll either start setting some funny flags or you'll repeat the
code.
if(getcursorpos(&pt) == -1)
{
exit(EXIT_FAILURE);
}
else if( ... )
makes it look like the main purpose of getcursorpos() is to return a
flag on which to make the decison whether or not to abort the program.
err = getcursorpos(&pt);
if(err == -1)
goto error_exit;
makes the situation a little clearer, though there's still more error
code than normal execution code.
The above code isn't complete enough. If each step augments a "transaction
in flight", and any step can fail before the final commit, one needs
"cascading rollback".
The "excessive nesting" way:
enum result
xact(void)
{
if (SUCCESS == op1()) {
if (SUCCESS == op2()) {
if (SUCCESS == op3()) {
commit(); /* can't fail */
return SUCCESS;
}
rollback2();
}
rollback1();
}
return FAIL;
}
Note that (a) this makes it easy to constrain declarations to the
narrowest applicable scopes, even under C89, and (b) rollbacks happen
naturally in reverse order. (The first thing one types into the
just-opened scope dependent on the SUCCESS of op_k() is rollback_k(), then
moves one line up and proceeds with calling and checking op_(k+1)(), or
with declarations necessary for that.)
The "goto way":
enum result
xact(void)
{
if (FAIL == op1()) {
goto bail0;
}
if (FAIL == op2()) {
goto bail1;
}
if (FAIL == op3()) {
goto bail2;
}
commit();
return SUCCESS;
bail2:
rollback2();
bail1:
rollback1();
bail0:
return FAIL;
}
Declarations can be constrained in C99 style (but that delays only the
beginnings, and doesn't bring the ends as near the top as desirable), or
by introducing "gratuitous" braces (which leads to nesting, which this
style tries to avoid). Reverse ordering of rollback steps needs extra
attention. (I think -- I never use this style, so one might mechanize it
easily as well.)
On the other hand, the quoted code can be completed like this (doing away
with "else"'s) -- among other ways:
enum result
xact(void)
{
if (FAIL == op1()) {
return FAIL;
}
if (FAIL == op2()) {
rollback1();
return FAIL;
}
if (FAIL == op3()) {
rollback2();
rollback1();
return FAIL;
}
commit();
return SUCCESS;
}
For n transactional steps, this needs O(n^2) rollback steps to be typed
out, and correct reverse ordering can be screwed up in about n-2 blocks.
'Nuff said.
Or else, the quoted code can be completed (among other ways) in this
different manner:
enum result
xact(void)
{
enum result res1,
res2 = FAIL,
res3 = FAIL;
res1 = op1();
if (SUCCESS == res1) {
res2 = op2();
}
if (SUCCESS == res2) {
res3 = op3();
}
if (SUCCESS == res3) {
commit();
return SUCCESS;
}
if (SUCCESS == res2) {
rollback2();
}
if (SUCCESS == res1) {
rollback1();
}
return FAIL;
}
A lot of unnecessary "if"'s can be executed. Explicit temporary variables
represent stretches of code that in fact correspond to nested scopes.
'Nuff said.
The only real contenders (from those mentioned) are the "deep nesting way"
and the "goto way". Due to its naturally narrow scoping of declarations
(even under C89 *and* wrt. scopes' ends), and due to its easy reverse
ordering of rollback steps, the nesting way wins for me.
lacos
You can "forget" about keeping track of an 'op2()' failure requiring a
'bail1' jump and a 'rollback1()' roll-back by grouping
operations/resources with their labels in the manner offered below. If
the time comes (such as in 'USE_BAZ') that a new resource is part of the
transaction, there aren't any indentation changes and previous code
remains untouched. That is, brand new code can be typed and the 'diff'
should not note any previous lines as being touched; pure additions.
Of course, that fact has different value to different people.
On Thu, 16 Sep 2010, Ersek, Laszlo wrote:
> The "excessive nesting" way:
>
> enum result
> xact(void)
> {
> if (SUCCESS == op1()) {
> if (SUCCESS == op2()) {
> if (SUCCESS == op3()) {
> commit(); /* can't fail */
> return SUCCESS;
> }
> rollback2();
> }
> rollback1();
> }
>
> return FAIL;
> }
> The "goto way":
>
> enum result
> xact(void)
> {
> if (FAIL == op1()) {
> goto bail0;
> }
>
> if (FAIL == op2()) {
> goto bail1;
> }
>
> if (FAIL == op3()) {
> goto bail2;
> }
>
> commit();
> return SUCCESS;
>
> bail2:
> rollback2();
>
> bail1:
> rollback1();
>
> bail0:
> return FAIL;
> }
In reality I usually like to log which step failed. I must honestly admit
the "goto way" reacts better to that modification -- its structure doesn't
change at all, while in my preferred way, the SUCCESS checks flip to FAIL
checks (less importantly), and for each "if", an "else" branch is added
(more importantly). With C99 style block scope declarations (and not
placing weight on how early visibilities end) I can see why one would like
the "goto way" more.
enum result
xact(void)
{
if (FAIL == op1()) {
log1();
}
else {
if (FAIL == op2()) {
log2();
}
else {
if (FAIL == op3()) {
log3();
}
else {
commit(); /* can't fail */
return SUCCESS;
}
rollback2();
}
rollback1();
}
return FAIL;
}
enum result
xact(void)
{
if (FAIL == op1()) {
log1();
goto bail0;
}
if (FAIL == op2()) {
log2();
goto bail1;
}
if (FAIL == op3()) {
log3();
goto bail2;
}
commit();
return SUCCESS;
bail2:
rollback2();
bail1:
rollback1();
bail0:
return FAIL;
}
lacos
It's not "total nonsense". It is a matter of preference or schools. I'm
with Dijkstra and you seem to be with Knuth in this matter.
/August
Dijkstra certainly didn't have those examples of good and well
thought-of goto usage in mind. When you want to point out a big mess,
you just HAVE to be radical in your first attempt.
; 0:Ok, !=0:error
; commit() has to follow the same error way above
xact:
.g: op1()|a#.z|op2()|a#.1|op3()|a#.2|commit()|#.z
.f: a=-1|#.z
.1: rollback1()|#.f
.2: rollback2()|#.1
.z:
ret
; 0:Ok, !=0:error
; commit() has to follow the same error way above
xactc:
.g: op1()|jc .z|op2()|jc .1|op3()|jc .2|commit()|#.z
.f: a=-1|#.z
.01: a= 1|#.z
.02: a= 2|#.z
.1: rollback1()|jc .01|#.f
.2: rollback2()|jc .02|#.1
.z:
ret
yes i'm agree with the goto one below
all the other threads example seems to me not
easy readable like that below
#define G goto
xact(void)
{if(FAIL==op1()) G b0;
if(FAIL==op2()) G b1;
if(FAIL==op3()) G b2;
commit(); return SUCCESS;
b2: rollback2(); // so these functions can not fail?
b1: rollback1();
b0: return FAIL;
> void function2(void)
> while(<condition1>)
> {
> while(<condition2>)
> {
> if(<condition3>)
> {
> goto continueouter;
> }
> }
> goto skipcontinue;
> continueouter:
> continue;
> skipcontinue:
> ...
> }
>
> }
>
>
Even if you must goto, The Elements of Programming Styles tells
us that you should never ever goto around another goto.
You should reverse the test and do a single jump.
But here even that doesn't appear necessary, if you put the
continue at the end.
void fun2 (void) {
while (<cond1>) {
while (<cond2>) {
if (<cond3>) goto outer;
}
/* ... */
outer: continue;
}
}
--
slow and steady ...
With brakes and gotos you cannot use loop invariants.
/August
Loop invariants are only useful inside ivory towers.
SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
Well, brakes and gotos are only useful inside jerk stores.
/August
You're clearly an amateur hacker if you really think that.
goto is a basic of any computer science and is more "absolute" than
anything else.
Misused its a pain yes. But for obvious efficient complex loop abort the
likes of break/goto are very very useful and server to simplify the
code.
I find brakes quite useful inside cars.
(Damn hard to resist, it was. Sorry.)
--
Marcin Grzegorczyk
This was mostly a silly joke from my side.
> You're clearly an amateur hacker if you really think that.
No, because a hacker is the very opposite; it's someone who uses and
misuses every feature of the language.
> goto is a basic of any computer science and is more "absolute" than
> anything else.
Yes, but at the ISA level.
/August