3

As an example:

function()
{
    calculation1();

    while(1)
    {
        if (test1() == FAIL) { break; }

        calculation2();

        if (test2() == FAIL) { break; }

        calculation3();

        if (test3() == FAIL) { break; }

        calculation4();

        break;
    }

    final_calculation();

    return;
}

Each test depends on results obtained from all calculations before it. If a test fails, however, the rest of the calculations should be skipped.

An alternative approach would be to use a series of nested if() statements:

function()
{
    calculation1();

    if (test1() == SUCCESS)
    {
        calculation2();

        if (test2() == SUCCESS)
        {
            calculation3();

            if (test3() == SUCCESS)
            {
                    calculation4();
            }
        }
    }

    final_calculation();

    return;
}

However this latter approach starts looking horribly messy in anything but a highly abstract example like above. I believe the former approach scales much better to longer, more complicated code. Are there any reasons not to go with the first method?

SharpHawk
  • 378
  • 7
  • 19
  • With a infinite loop you have the risk that it will be infinite! with if statements not, they are just true or false but the code goes forward – Rizier123 Nov 18 '14 at 20:10
  • Matter of opinion, but I've been known to do it. You should always see if it can be written more clearly to not use `break`, though – The Archetypal Paul Nov 18 '14 at 20:11
  • 1
    I think this is fine, but it might be even better to put those into a function. Then you can `return` instead of `break`. – Zan Lynx Nov 18 '14 at 20:13
  • 7
    I don't think it is nice to disguise a `goto` where a `goto` is appropriate. This is standard C, with no construct for exception handling: `goto` is a way to solve this problem when such kind of code is necessary (just take a look in the Linux kernel's source, you may also like this article: http://blog.regehr.org/archives/894). – Jubatian Nov 18 '14 at 20:17
  • possible duplicate of [How to avoid "if" chains?](http://stackoverflow.com/questions/24430504/how-to-avoid-if-chains) – anatolyg Nov 18 '14 at 20:21
  • @anatolyg: It almost is, but that question mixes C and C++. The two languages offer different set of possible solutions, however as I see the answers cover C specific stuff (there I would have liked to see proper exception handling demonstrated, why it is missing? I have little C++ knowledge though...). – Jubatian Nov 18 '14 at 20:31
  • What exceptions? calling a function and (possibly) returning FAIL is not an exception. – user3629249 Nov 19 '14 at 05:34
  • @user3629249: Nothing requires you to make the *function* throwing an exception if you solve it's innards with it. You can always `catch` everything, and assemble a nice return value (I even like `goto` in C for this reason: there you simply must do it this way, so there is no way to turn the C code in an exception swamp). – Jubatian Nov 19 '14 at 07:40

3 Answers3

5

I have no problem with that technique, and use it frequently.

However, I typically format it as:

    bool success = false;  // Pessimistically assume we will fail
    do
    {
        calculation1();
        if (test1() == FAIL)
        {
            break;
        }

        calculation2();
        if (test2() == FAIL)
        {
            break;
        }

        calculation3();
        if (test3() == FAIL)
        {
            break;
        }

        calculation4();
        success = true;  // Note the success!
    } while(false);

    // TODO: Check the success-variable to know if we failed early, or went all the way through.
    final_calculation();

    return;
}

(Sometimes my co-workers don't like a loop with a while(false) condition)

abelenky
  • 63,815
  • 23
  • 109
  • 159
3

A goto is a goto. You should just use it, rather than hiding it with a while loop.

And yes it is considered harmful, but in this case there's no real way to avoid it without a much larger code refactoring.

That's the real problem and it may not be worth fixing.

  • 2
    Although I would tentatively approve of goto in this situation, I've had co-workers who saw one goto... believed that goto was okay, and added more and more gotos until we had a velociraptor outbreak on our hands. Hence, I avoid goto ***always***. In this case, `do-while(false)` with `break;` works just fine. I suspect that in Assembly, the break statements are absolutely identical to the goto-statements, with the additional safety that you can never accidentally "break" backwards. You can accidentally goto backwards, however. – abelenky Nov 18 '14 at 20:35
1

An alternative

failed = false

if (!failed) {
   calculation1();
   if (test1() != SUCCESS)
      failed = true;
}
 if (!failed) {
   calculation2();
   if (test2() != SUCCESS)
      failed = true;
}
if (!failed) {
   calculation3();
   if (test3() != SUCCESS)
      failed = true;
}
if (!failed) {
   calculation4();
  }

final_calculation();

(!failed check on the first just for symmetry)

The Archetypal Paul
  • 41,321
  • 20
  • 104
  • 134
  • This method is very explicit which means a lot but it also has a different performance than the original two examples. – yo.ian.g Nov 18 '14 at 20:20
  • True, but it would require calculationN to be very trivial indeed before the costs of the tests and assignments made any noticeable difference. – The Archetypal Paul Nov 18 '14 at 20:24
  • This code does not produce the same results as mine. The function final_calculation() should not reside in an if() statement, as it should always be executed. Replace final_calculation() with calculation4() and append final_calculation to the end of the code and that should be correct. – SharpHawk Nov 19 '14 at 15:07