当代码流是这样的:

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

我通常看到这种方法可以避免上述混乱的代码流:

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

有什么更好的方法可以避免这种工作/黑客,从而使其成为更高级别(行业级别)的代码?

任何跳出盒子的建议都是欢迎的!


当前回答

你可以使用一个简单的bool变量的延续模式:

bool goOn;
if ((goOn = check0())) {
    ...
}
if (goOn && (goOn = check1())) {
    ...
}
if (goOn && (goOn = check2())) {
    ...
}
if (goOn && (goOn = check3())) {
    ...
}

这个执行链将在checkN返回false时停止。由于&&操作符短路,将不再执行进一步的检查…()调用。此外,优化编译器足够聪明,可以识别将goOn设置为false是单行道,并为您插入缺少的goto结尾。因此,上面代码的性能将与do/while(0)相同,只是对其可读性没有严重的影响。

其他回答

对我来说,当(0)是好的时候做{…}。如果您不想看到do{…}while(0),您可以为它们定义替代关键字。

例子:

SomeUtilities.hpp:

#define BEGIN_TEST do{
#define END_TEST }while(0);

SomeSourceFile.cpp:

BEGIN_TEST
   if(!condition1) break;
   if(!condition2) break;
   if(!condition3) break;
   if(!condition4) break;
   if(!condition5) break;
   
   //processing code here

END_TEST

我认为编译器将在二进制版本的do{…}while(0)中删除不必要的while(0)条件,并将断点转换为无条件跳转。您可以检查它的汇编语言版本来确定。

使用goto还可以生成更简洁的代码,并且使用条件-然后跳转的逻辑非常简单。您可以执行以下操作:

{
   if(!condition1) goto end_blahblah;
   if(!condition2) goto end_blahblah;
   if(!condition3) goto end_blahblah;
   if(!condition4) goto end_blahblah;
   if(!condition5) goto end_blahblah;
   
   //processing code here

 }end_blah_blah:;  //use appropriate label here to describe...
                   //  ...the whole code inside the block.
 

注意标签放在结束}之后。这是避免一个可能的问题,在goto中,不小心将代码放在中间,因为你没有看到标签。现在就像do{…}while(0)没有条件代码。

为了让代码更清晰,更容易理解,你可以这样做:

SomeUtilities.hpp:

#define BEGIN_TEST {
#define END_TEST(_test_label_) }_test_label_:;
#define FAILED(_test_label_) goto _test_label_

SomeSourceFile.cpp:

BEGIN_TEST
   if(!condition1) FAILED(NormalizeData);
   if(!condition2) FAILED(NormalizeData);
   if(!condition3) FAILED(NormalizeData);
   if(!condition4) FAILED(NormalizeData);
   if(!condition5) FAILED(NormalizeData);

END_TEST(NormalizeData)

有了这个,你可以做嵌套块,并指定你想要退出/跳出的位置。

BEGIN_TEST
   if(!condition1) FAILED(NormalizeData);
   if(!condition2) FAILED(NormalizeData);

   BEGIN_TEST
      if(!conditionAA) FAILED(DecryptBlah);
      if(!conditionBB) FAILED(NormalizeData);   //Jump out to the outmost block
      if(!conditionCC) FAILED(DecryptBlah);
  
      // --We can now decrypt and do other stuffs.

   END_TEST(DecryptBlah)

   if(!condition3) FAILED(NormalizeData);
   if(!condition4) FAILED(NormalizeData);

   // --other code here

   BEGIN_TEST
      if(!conditionA) FAILED(TrimSpaces);
      if(!conditionB) FAILED(TrimSpaces);
      if(!conditionC) FAILED(NormalizeData);   //Jump out to the outmost block
      if(!conditionD) FAILED(TrimSpaces);

      // --We can now trim completely or do other stuffs.

   END_TEST(TrimSpaces)

   // --Other code here...

   if(!condition5) FAILED(NormalizeData);

   //Ok, we got here. We can now process what we need to process.

END_TEST(NormalizeData)

杂乱的代码不是goto的错,而是程序员的错。不使用goto仍然可以生成意大利面条代码。

我对这里提出的不同答案的数量感到惊讶。但是,最后在我必须更改的代码中(即删除这个do-while(0)黑客或任何东西),我做了一些与这里提到的任何答案不同的事情,我很困惑为什么没有人想到这一点。以下是我所做的:

初始代码:

do {

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

finishingUpStuff.

Now:

finish(params)
{
  ...
  ...
}

if(!check()){
    finish(params);    
    return;
}
...
...
if(!check()){
    finish(params);    
    return;
}
...
...
if(!check()){
    finish(params);    
    return;
}
...
...

所以,这里所做的是,整理的东西被隔离在一个函数中,事情突然变得如此简单和干净!

我认为这个解决方案值得一提,所以在这里提供了它。

如果你不需要在执行过程中引入局部变量,那么你通常可以将其平化:

if (check()) {
  doStuff();
}  
if (stillOk()) {
  doMoreStuff();
}
if (amIStillReallyOk()) {
  doEvenMore();
}

// edit 
doThingsAtEndAndReportErrorStatus()
typedef bool (*Checker)();

Checker * checkers[]={
 &checker0,&checker1,.....,&checkerN,NULL
};

bool checker1(){
  if(condition){
    .....
    .....
    return true;
  }
  return false;
}

bool checker2(){
  if(condition){
    .....
    .....
    return true;
  }
  return false;
}

......

void doCheck(){
  Checker ** checker = checkers;
  while( *checker && (*checker)())
    checker++;
}

怎么样?

类似于dasblinkenlight的答案,但避免了if语句中的赋值,这可能会被代码评审员“修复”:

bool goOn = check0();
if (goOn) {
    ...
    goOn = check1();
}
if (goOn) {
    ...
    goOn = check2();
}
if (goOn) {
    ...
}

...

当一个步骤的结果需要在下一步之前检查时,我使用这种模式,这与所有检查都可以在前面用一个大if(check1() && check2()…类型的模式。