Alternatives to `while (1)` to simplify branching

C

C Problem Overview


From time to time, I use a while(1) block to flatten a succession of if..else going out of proportion. It goes along these lines.

Instead of doing:

// process 
if (success) {
  // process 
  if (success) {
    //process
    if (success) {
      // etc
    }
  }
}

I do:

while (1) {
  // process
  if (!success) break;
  // process
  if (!success) break;
  // process
  if (!success) break;
  // etc
  break;
}

I am a little annoyed by the implicit jump at the end of the while. Could I get away with a leaner construct (ie no break at the end)?

I could trade the final break with a variable (or register?). That's not exactly leaner or clearer.

int once = 1;
while (once--) {
  // process
  if (!success) break;
  // process
  if (!success) break;
  // process
  if (!success) break;
  // etc
}

A for loop would look a bit better (C99):

for (int once = 1 ; once--; once) {
  // process
  if (!success) break;
  // process
  if (!success) break;
  // process
  if (!success) break;
  // etc
}

I thought about using a switch case. It does not look much better , though it would work.

switch (1) { default:
  // process
  if (!success) break;
  // process
  if (!success) break;
  // process
  if (!success) break;
  // etc
}

In that particular case the concept of a label seems unbeatable.

// process
if (!success) goto end;
// process
if (!success) goto end;
// process
if (!success) goto end;
// etc

end:

What other approach do you guys know/use?

C Solutions


Solution 1 - C

>What other approach do you guys know/use?

You can encapsulate your while loop in a function (and call this function where you had your while loop):

static void process(void)
{
   // process
   if (!success) return;
   // process
   if (!success) return;
   // process
   if (!success) return;
   // process
}

Any halfway decent compiler (e.g., even gcc with optimizations disabled) will inline a static function if it is called once. (Of course some variables may have to be in the lexical scope of process function, in that case just provide them as parameters of the function).

Note that writing code from top to bottom instead of horizontally (e.g., your example with nested if) is called duffing. There is a nice article on the subject here:

"Reading Code From Top to Bottom"

Also, in the Linux kernel coding style there is a specific warning writinh against horizontal code:

>"if you need more than 3 levels of indentation, you're screwed anyway, and should fix your program"

Solution 2 - C

The following is a method very similar to what you're doing with the loops, but without the need for a counter or a break statement at the end.

do
{
    // process
    if (!success) break;
    // process
    if (!success) break;
    // process
    if (!success) break;
    ...
    // No need for a break statement here
}
while(0);

Solution 3 - C

If you arrange that the body of each conditional block generating success is a function as follows or each // process can otherwise be reduced to a boolean expression, such as:

success = f1() ; 
if( success ) 
{
  success = f2() ; 
  if( success ) 
  {
    success = f3() ; 
    if( success ) 
    {
      success = f4()
    }
  }
}

Then you can reduce this to a single boolean expression exploiting short-circuit evaluation:

success = f1() && 
          f2() && 
          f3() && 
          f4() ;

Here f2() will not be called if f1() returns false and the same for each successive call - expression evaluation aborts on the first && operand sub-expression to return false.

Solution 4 - C

Not clear why you'd need to nest or break. I do this all the time when a sequence needs to bail at first failure:

// process

if (success) {
  // more process
}

if (success) {
  // still more process
}

if (success) {
  // even more process
}

Solution 5 - C

Fiddling bits has provided a common approach. Another common approach is to use a single status variable/flag to achieve a similar result.

bool bErr = false;

if (!bErr && success) {
   // do something
} else {
   bErr = true;
}
if (!bErr && success2) {
   // do something
} else {
   bErr = true;
}

if (bErr) {
   // hanlde cleanup from errors
}

Solution 6 - C

Another option would be using a simple flag variable

bool okay = true;

if(okay &= success){
    // process
}

if(okay &= success){
    // process
}

if(okay &= success){
    // process
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPhilippe A.View Question on Stackoverflow
Solution 1 - CouahView Answer on Stackoverflow
Solution 2 - CFiddling BitsView Answer on Stackoverflow
Solution 3 - CCliffordView Answer on Stackoverflow
Solution 4 - CPaul RoubView Answer on Stackoverflow
Solution 5 - CCloudView Answer on Stackoverflow
Solution 6 - CfrogattoView Answer on Stackoverflow