C ++,这个goto语句是否合理?

时间:2010-07-27 17:01:22

标签: c++ exception-handling goto

我稍微改变了标题,因为我认为这是更合适的问题。

你会重构它(似乎合法使用goto)? 如果,您将如何重构以下代码以删除转到语句?

if (data.device) {
    try {
        ...
    }
    catch(const std::exception&) { goto done; }
    ... // more things which should not be caught
done: ;
}

完整陈述

#ifdef HAVE_GPU
            // attempt to use GPU device
            if (data.device) {
                try {
                    Integral::Gpu eri(S, R, Q, block.shell());
                    eri(basis.centers(), quartets, data.device);
                }
                // if GPU fails, propagate to cpu
                catch(std::exception) { goto done; }
                data.device += size;
                host_index.extend(block_index);
                block_index.data.clear();
            done: ;
            }
#endif

谢谢

在看过大多数人的偏好之后,我决定带着旗帜,但约克先生发表评论。

谢谢大家

14 个答案:

答案 0 :(得分:15)

if (data.device)
{
    bool status = true;

    try
    {
        ...
    }
    catch(std::exception)
    {
        status = false;
    }

    if (status)
    {
    ... // more things which should not be caught
    }
}

答案 1 :(得分:8)

首先:goto本身并不邪恶。为了在代码中没有字母“goto”的纯粹重构而进行重构是无稽之谈。将它重构为能够完成与goto相同的事情的东西很好。将糟糕的设计改为更好的设计并且不需要goto或替换它也很好。

话虽如此,我会说你的代码看起来与最终发明的代码完全一样。太可悲了C ++没有这样的东西......所以最简单的解决办法就是保持这样。

答案 2 :(得分:7)

您可以捕获异常并重新抛出可以在条件块之外处理的特定异常。

// obviously you would want to name this appropriately...
struct specific_exception : std::exception { };

try {
    if (data.device) {
        try {
            // ...
        }
        catch(const std::exception&) { 
            throw specific_exception(); 
        }

        // ... more things which should not be caught ...
    }
}
catch (const specific_exception&) { 
    // handle exception
}

答案 3 :(得分:4)

为什么不在try块中移动额外的代码?:

#ifdef HAVE_GPU
            // attempt to use GPU device
            if (data.device) {
                try {
                    Integral::Gpu eri(S, R, Q, block.shell());
                    eri(basis.centers(), quartets, data.device);
                    data.device += size;
                    host_index.extend(block_index);
                    block_index.data.clear();
                }
                // if GPU fails, propagate to cpu
                catch(std::exception) { /* handle your exception */; }
            }
#endif

答案 4 :(得分:4)

我认为这个的变体可能适合你。

// attempt to use GPU device
if (data.device)
{
    try
    {
        Integral::Gpu eri(S, R, Q, block.shell());
        eri(basis.centers(), quartets, data.device);

        data.device += size;
        host_index.extend(block_index);
        block_index.data.clear();
    }
    catch (const std::bad_alloc&)
    {
        // this failure was not because 
        // of the GPU, let it propagate
        throw;
    }
    catch(...)
    {
        // handle any other exceptions by
        // knowing it was the GPU and we 
        // can fall back onto the CPU.
    }
}

// do CPU

如果您可以编辑GPU库并将所有GPU异常作为gpu_exception之类的基础,则代码变得更加简单:

// attempt to use GPU device
if (data.device)
{
    try
    {
        Integral::Gpu eri(S, R, Q, block.shell());
        eri(basis.centers(), quartets, data.device);

        data.device += size;
        host_index.extend(block_index);
        block_index.data.clear();
    }
    catch (const gpu_exception&)
    {
        // handle GPU exceptions by
        // doing nothing and falling
        // back onto the CPU.
    }

    // all other exceptions, not being 
    // GPU caused, may propagate normally
}

// do CPU

如果这些工作更加深入,我认为下一个最好的事情是Steve's answer

答案 5 :(得分:4)

使用旗帜略有不同。我认为它比Amardeep更整洁。

我宁愿使用一个标志来指示是否传播异常,而不是一个标志来指示最后一件事是否有效,因为异常的整点是为了避免检查是否是最后一个事情是有效的 - 想法是编写代码,如果我们到目前为止,那么一切都有效,我们很高兴继续。

#ifdef HAVE_GPU
    // attempt to use GPU device
    if (data.device) {
        bool dont_catch = false;
        try {
            ...
            dont_catch = true;
            ... // more things which should not be caught
        } catch (...) {
            if (dont_catch) throw;
        }
    }
#endif

答案 6 :(得分:3)

if (data.device) {
    bool ok = true;
    try {
        ...
    }
    catch(std::exception) { ok = false; }

    if(ok) {
        ... // more things which should not be caught
    }
}

答案 7 :(得分:2)

你能否在if?

之外捕获异常?

答案 8 :(得分:2)

当有一堆代码需要根据某些条件退出时,我首选的构造是在适当时使用“do {} while(0)”循环和“break”。我不知道休息了什么;但是,他会做一件事。如果“休息”不起作用,“goto”可能是你最好的选择。

答案 9 :(得分:2)

我是否遗漏了某些内容,或者它不等同于在catch块内的done:try标签之间移动部分?

#ifdef HAVE_GPU
            // attempt to use GPU device
            if (data.device) {
                try {
                    Integral::Gpu eri(S, R, Q, block.shell());
                    eri(basis.centers(), quartets, data.device);
                    data.device += size;
                    host_index.extend(block_index);
                    block_index.data.clear();
                }
                // if GPU fails, propagate to cpu
                catch(std::exception) {}
            }
#endif

答案 10 :(得分:1)

使用一些标志并添加条件语句怎么样?

int caught = 0;
if (data.device) {
    try {
        /* ... */
    } catch (std::exception e) { caught = 1; }
    if (!caught) {
        // more stuff here
    }
    // done: ...
}

答案 11 :(得分:1)

catch(std::exception) { return; }

这应该可以解决问题。当然,我假设done确实在函数的末尾。

如果您需要在发生异常时执行其他代码,请返回状态或抛出更合适的抽象级别的异常(请参阅James的回答)。

我想象的是:

doStuff(...) {
    bool gpuSuccess = doGPUStuff(...);
    if (!gpuSuccess) {
        doCPUStuff(...);
    }
}

答案 12 :(得分:1)

将其分解为自己的函数/方法(包括其后的所有内容)并使用return关键字。只要你的所有变量都有析构函数,就是堆栈分配(或者如果不可避免地指向智能指针)那么你就没事了。我是早期退出功能/方法的忠实粉丝,而不是持续的旗帜检查。

例如:

void myFunc()
{
    String mystr("heya");
    try
    {
        couldThrow(mystr);
    }
    catch(MyException& ex)
    {
        return; // String mystr is freed upon returning
    }

    // Only execute here if no exceptions above
    doStuff();
}

这样,很难出错

答案 13 :(得分:1)

今天goto不赞成的原因是我们将这些奇特的东西称为“功能”。将GPU代码包装在自己的函数中,如果失败则可以提前返回。

然后从原始功能中调用它。

因为他们可能需要共享一些变量(datahost_indexblock_index,看起来像),将它们放在一个类中,并使这两个函数成员它的。

void RunOnGpu(){
        // attempt to use GPU device
        if (data.device) {
            try {
                Integral::Gpu eri(S, R, Q, block.shell());
                eri(basis.centers(), quartets, data.device);
            }
            // if GPU fails, propagate to cpu
            catch(std::exception) { return; }
            data.device += size;
            host_index.extend(block_index);
            block_index.data.clear();     
}
void DoStuff() {
#ifdef HAVE_GPU
    RunOnGpu();
#endif
}