What are some better ways to avoid the do-while(0); hack in C++?

When the code flow is like this:

if(check())
{
  ...
  ...
  if(check())
  {
    ...
    ...
    if(check())
    {
      ...
      ...
    }
  }
}

I have generally seen this work around to avoid the above messy code flow:

do {
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
} while(0);

What are some better ways that avoid this work-around/hack so that it becomes a higher-level (industry level) code?

Any suggestions which are out of the box are welcome!


Solution 1:

It is considered acceptable practice to isolate these decisions in a function and use returns instead of breaks. While all these checks correspond to the same level of abstraction as of the function, it is quite logical approach.

For example:

void foo(...)
{
   if (!condition)
   {
      return;
   }
   ...
   if (!other condition)
   {
      return;
   }
   ...
   if (!another condition)
   {
      return;
   }
   ... 
   if (!yet another condition)
   {
      return;
   }
   ...
   // Some unconditional stuff       
}

Solution 2:

There are times when using goto is actually the RIGHT answer - at least to those who are not brought up in the religious belief that "goto can never be the answer, no matter what the question is" - and this is one of those cases.

This code is using the hack of do { ... } while(0); for the sole purpose of dressing up a goto as a break. If you are going to use goto, then be open about it. It's no point in making the code HARDER to read.

A particular situation is just when you have a lot of code with quite complex conditions:

void func()
{
   setup of lots of stuff
   ...
   if (condition)
   {
      ... 
      ...
      if (!other condition)
      {
          ...
          if (another condition)
          {
              ... 
              if (yet another condition)
              {
                  ...
                  if (...)
                     ... 
              }
          }
      }
  .... 

  }
  finish up. 
}

It can actually make it CLEARER that the code is correct by not having such a complex logic.

void func()
{
   setup of lots of stuff
   ...
   if (!condition)
   {
      goto finish;
   }
   ... 
   ...
   if (other condition)
   {
      goto finish;
   }
   ...
   if (!another condition)
   {
      goto finish;
   }
   ... 
   if (!yet another condition)
   {
      goto finish;
   }
   ... 
   .... 
   if (...)
         ...    // No need to use goto here. 
 finish:
   finish up. 
}

Edit: To clarify, I'm by no means proposing the use of goto as a general solution. But there are cases where goto is a better solution than other solutions.

Imagine for example that we are collecting some data, and the different conditions being tested for are some sort of "this is the end of the data being collected" - which depends on some sort of "continue/end" markers that vary depending on where you are in the data stream.

Now, when we're done, we need to save the data to a file.

And yes, there are often other solutions that can provide a reasonable solution, but not always.