怎么写功能整齐?考虑记录,一次返回,没有更多的缩进?

时间:2011-10-26 15:34:57

标签: c++ logging return goto

有一个功能Foo。 Foo首先调用Bar(),然后调用一些语句,然后是Walk(),然后是一些语句,然后是Run(),然后是一些语句,然后是Fly(),然后是一些语句。

此外我不允许在我的代码中使用异常。因为我公司的代码标准。 :(

有几种方法,我可以编写Foo(),如下所示。有些很干净但不安全;有些人真的很难看,但记录了所有的日志。我真的很困惑。

哪一个最好,你有更好的想法吗?

任何人都可以帮助我吗?

我从五个方向考虑过这个问题:(清除[没有不必要的缩进]),(一次返回),(没有转到),(打印日志),(检查返回值)。但我仍然感到困惑。 goto真的有问题吗?是否有必要记录所有内容?

FIG1 优点:清洁。 缺点:没有日志&不检查返回值。

int Foo()
{
    Bar();
    // aStatement;
    Walk();
    // bStatement;
    Run();
    // cStatement;
    Fly();
    // dStatement;
    return;
}

fig2 优点:清洁&日志。 缺点:多回报。

int Foo() {
    int status;
    status = Bar();
    if (!OK(status)) {
        Log("...");
        return status;
    }
    // aSatement
    status = Walk();
    if (!OK(status)) {
        Log("...");
        return status;
    }
    // bStatement
    status = Run();
    if (!OK(status)) {
        Log("...");
        return status;
    }
    // cStatement
    status = Fly();
    if (!OK(status)) {
        Log("...");
        return status;
    }
    // dSatement
    return status;
}

图三 优点:清洁&日志。 缺点:转到问题。

int Foo() {
    int status;
    status = Bar();
    if (!OK(status)) {
        Log("...");
        goto RETLINE;
    }
    // aSatement
    status = Walk();
    if (!OK(status)) {
        Log("...");
        goto RETLINE;
    }
    // bStatement
    status = Run();
    if (!OK(status)) {
        Log("...");
        goto RETLINE;
    }
    // cStatement
    status = Fly();
    if (!OK(status)) {
        Log("...");
        goto RETLINE;
    }
    // dSatement
RETLINE:
    return status;
}

图四 优点:使用do while,没有多重回报。 缺点:更多缩进。

int Foo() {
    int status;

    do {
        status = Bar();
        if (!OK(status)) {
            Log("...");
            break;
        }
        // aStatement
        status = Walk();
        if (!OK(status)) {
            Log("...");
            break;
        }
        status = Run();
        if (!OK(status)) {
            Log("...");
            break;
        }
        // cStatement
        status = Fly();
        if (!OK(status)) {
            Log("...");
            break;
        }
        // dStatement
    } while(false);
    return status;
}

图5 优点:清洁&一回归。 缺点:没有记录。

int Foo() {
    int status;
    status = Bar();
    if (Ok(status)) {
        status = Walk();
        // aStatement
    }
    if (OK(status)) {
        // bStatement
        status = Run();
    }
    if (OK(status)) {
        // cStatement
        status = Fly();
    }
    if (Ok(status)) {
        // dStatement
    }
    return status;
}

图6 优点:日志&一回归。 缺点:更多缩进。

int Foo() {
    int status;

    status = Bar();
    if (!OK(status)) {
        Log("...");
    }
    if (Ok(status)) {
        // aStatement
        status = Walk();
        if (!OK(status)) {
            Log("...");
        }
    }
    if (OK(status)) {
        // bStatement
        status = Run();
        if (!OK(status)) {
            Log("...");
        }
    }
    if (Ok(status)) {
        // cStatement
        status = Fly();
        if (!OK(status)) {
            Log("...");
        }
    }
    if (Ok(status)) {
        // dStatement
    }
    return status;
}

fig7 优点:日志&一回归。 缺点:更多缩进。

int Foo() {
    bool flag = true;
    int status;
    status = Bar();
    if (!OK(status)) {
        Log("...");
        flag = false;
    }
    if (flag) {
        // aStatement
        status = Walk();
        if (!OK(status)) {
            Log("...");
            flag = false;
        }
    }
    if (flag) {
        // bStatement
        status = Run();
        if (!OK(status)) {
            Log("...");
            flag = false;
        }
    }

    if (flag) {
        // cStatement
        status = Fly();
        if (!Ok(status)) {
            Log("...");
            flag = false;
        }
    }
    if (flag) {
        // dStatment
    }
    return status;
}

6 个答案:

答案 0 :(得分:3)

答案 1 :(得分:0)

我使用规则:首先是有效性,然后是健壮性,然后是可维护性,然后在需要时效率:永远不要为了更清晰的代码而牺牲日志。当您的应用程序在某些情况下没有行走(或跑步或飞行)时,您需要日志。

从提案中,我建议使用早期返回,但与RAII结合使用,因此不需要在函数中进行清理。这非常接近使用例外。

BTW:使用goto有一个单独的退出点来清理并不是很糟糕,但在使用RAII时不再需要了。

答案 2 :(得分:0)

由于您的所有功能都具有相同的签名,您可以将呼叫和状态检查包装在一个功能中,并使用&&。

链接所有内容。
int try_or_log(int (*f) (), const char** message) {
       status = (*f)();
       if(!Ok(status) {
          Log(message);
       return status;
}

int Foo() 
{
    return try_or_log(&Bar, "...") &&
        try_or_log(&Walk, "...") &&
        try_or_log(&Fly, "...");
}

(我的C ++生锈了,可能无法编译) 否则,这似乎是Monad的一个很好的候选者,所以你可以编写一个代码看起来像的对象(这可能值得谷歌'Monda in C ++'

int Foo() {
  Status status;
  status << &Bar << "..." << &Walk << "I don't walk" << &Fly << "I don't fly";
  return status.status;
}

(实际上你可能需要2个类,所以&lt;&lt;运算符将根据输入(函数或日志消息)返回一个或另一个类。

答案 3 :(得分:0)

我个人会使用#2。我不认为退出函数会使执行路径变得复杂,足以证明您概述的任何其他方法。记录取决于您。更复杂的事情需要更多的记录。也许您可以将日志移动到可能失败的功能中? 你考虑过例外吗?

绝对不要使用break循环技巧。当你真正创建一个范围时,这似乎就是你的循环。

我不是100%反对goto。它对于快速修复很方便,但我不会设计代码来使用它。

旗帜似乎只是简单的额外包袱。

无论你决定坚持哪种惯例,

答案 4 :(得分:0)

如果你不能或不想使用例外,我会选择fig2或fig7。我从来没有见过一个很好的论据来反对一个方法中的多个返回,并且它做了你想要的其他一切,但如果你不喜欢这种方法或你有一个禁止它的风格指南,那么使用一个标志是可以接受的,尽管创建了一个对于这么简单的事情,不必要的变量。

使用do something() while(false)似乎很愚蠢,因为你没有使用循环。

有些人对goto非常虔诚,所以除非你别无选择,否则最好避免它,即使只是为了避免争论。它本身并不是邪恶的,但假设它可能更安全。

答案 5 :(得分:0)

如果你必须检查返回值,那么方法1就不够了。

方法2是可以接受的。

方法3是淫秽。

方法4-7只是方法2,过于复杂以避免多个return,这些并不是那么糟糕。

如果您允许修改Bar()Walk()等,请考虑例外情况。异常与此代码的行为不同,因为调用者可以忽略Foo的返回值,而异常则不能;这可能适合您的目的,也可能不适合。