我稍微改变了标题,因为我认为这是更合适的问题。
你会重构它(似乎合法使用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
谢谢
在看过大多数人的偏好之后,我决定带着旗帜,但约克先生发表评论。谢谢大家
答案 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代码包装在自己的函数中,如果失败则可以提前返回。
然后从原始功能中调用它。
因为他们可能需要共享一些变量(data
,host_index
和block_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
}