当代码流是这样的:

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)相同,只是对其可读性没有严重的影响。

其他回答

从函数式编程的角度来看,这是一个众所周知的、很好解决的问题——也许是单子。

为了回应下面我收到的评论,我在这里编辑了我的介绍:你可以在不同的地方找到实现c++单子的完整细节,这将让你实现Rotsor建议的目标。这需要一段时间来理解单子,所以我要在这里建议一个快速的“穷人”单子式机制,你只需要知道boost::optional。

设置你的计算步骤如下:

boost::optional<EnabledContext> enabled(boost::optional<Context> context);
boost::optional<EnergisedContext> energised(boost::optional<EnabledContext> context);

显然,每个计算步骤都可以执行类似于返回boost::none这样的操作,如果给出的可选参数为空的话。例如:

struct Context { std::string coordinates_filename; /* ... */ };

struct EnabledContext { int x; int y; int z; /* ... */ };

boost::optional<EnabledContext> enabled(boost::optional<Context> c) {
   if (!c) return boost::none; // this line becomes implicit if going the whole hog with monads
   if (!exists((*c).coordinates_filename)) return boost::none; // return none when any error is encountered.
   EnabledContext ec;
   std::ifstream file_in((*c).coordinates_filename.c_str());
   file_in >> ec.x >> ec.y >> ec.z;
   return boost::optional<EnabledContext>(ec); // All ok. Return non-empty value.
}

然后把它们串在一起:

Context context("planet_surface.txt", ...); // Close over all needed bits and pieces

boost::optional<EnergisedContext> result(energised(enabled(context)));
if (result) { // A single level "if" statement
    // do work on *result
} else {
    // error
}

这样做的好处是,您可以为每个计算步骤编写定义清晰的单元测试。此外,调用读起来像简单的英语(通常是函数式风格的情况)。

如果你不关心不可变性,并且每次返回相同的对象更方便,你可以使用shared_ptr或类似的方法来提出一些变化。

为什么标记法没有被回答,这是自古以来使用的方法。

//you can use something like this (pseudocode)
long var = 0;
if(condition)  flag a bit in var
if(condition)  flag another bit in var
if(condition)  flag another bit in var
............
if(var == certain number) {
Do the required task
}

当然不是答案,而是一个答案(为了完整起见)

而不是:

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

你可以这样写:

switch (0) {
case 0:
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
}

这仍然是一个伪装的goto,但至少它不再是一个循环。这意味着你不需要非常仔细地检查是否有一些继续隐藏在块的某处。

构造也非常简单,您可以希望编译器会优化它。

正如@jamesdlin所建议的,你甚至可以把它隐藏在一个宏后面

#define BLOC switch(0) case 0:

然后像这样使用它

BLOC {
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
}

这是可能的,因为C语言语法期望在开关之后有一个语句,而不是一个括号中的块,并且您可以在该语句之前放置一个case标签。到目前为止,我还没有看到允许这样做的意义,但在这种特殊情况下,将开关隐藏在一个漂亮的宏后面是很方便的。

如果你的代码有一个很长的If ..else If ..else语句块,你可以尝试在函子或函数指针的帮助下重写整个块。这可能并不总是正确的解决方案,但通常是正确的。

http://www.cprogramming.com/tutorial/functors-function-objects-in-c++.html

如何移动if语句到一个额外的函数,产生一个数值或enum结果?

int ConditionCode (void) {
   if (condition1)
      return 1;
   if (condition2)
      return 2;
   ...
   return 0;
}


void MyFunc (void) {
   switch (ConditionCode ()) {
      case 1:
         ...
         break;

      case 2:
         ...
         break;

      ...

      default:
         ...
         break;
   }
}