避免"如果清理失败"重复的模式
Pattern to avoid "if failed cleanup" repetition
我有一些代码看起来像这样:
int myfunc()
{
blah a;
blah2 b;
blah3 c;
blah4 d;
blah5 e;
int iRes = DoSomething1(a, b, c);
if (iRes > 0)
{
clean1(a, b, c);
clean2(d, e);
log_error();
return 1;
}
iRes = DoSomething2(a, c, e);
if (iRes > 0)
{
clean1(a, b, c);
clean2(d, e);
log_error();
return 1;
}
...
iRes = DoSomething10(c, d, e);
if (iRes > 0)
{
clean1(a, b, c);
clean2(d, e);
log_error();
return 1;
}
clean1(a, b, c);
clean2(d, e);
return 0;
}
在 C 或 C++ 中,如何避免在每个函数调用后重复if (iRes > 0) { clean1(a, b, c); clean2(d, e); log_error(); return 1; }
?
笔记:
- 在实际代码中,这些函数
DoSomethingx()
和cleanx()
都是API函数,不是我自己写的 - 我想避免在
myfunct()
之外定义第二个函数clean()
处理清理+错误 - 我考虑过使用预处理器宏,但我怀疑在这种情况下这是一个很好的做法
例:
这段代码就是这种情况的一个例子:每 10 行代码实际上 =2 行仅用于实际执行某事+ 8 行错误测试和清理......我们能做得更好吗?
正如贾斯汀所提到的,答案会因语言而异。
如果你在C语言中,这是goto
在该语言中占有据点的最终位置之一。例如,如果您有:
int myfunc()
{
blah a;
blah2 b;
blah3 c;
blah4 d;
blah5 e;
int iRes = DoSomething1(a, b, c);
if (iRes > 0)
{
goto error_cleanup;
}
iRes = DoSomething2(a, c, e);
if (iRes > 0)
{
goto error_cleanup;
}
/*...*/
iRes = DoSomething10(c, d, e);
if (iRes > 0)
{
goto error_cleanup;
}
/* SUCCESS */
clean1(a, b, c);
clean2(d, e);
return 0;
/* ERROR EXIT POINT */
error_cleanup:
clean1(a, b, c);
clean2(d, e);
log_error();
return 1;
}
但是,在C++即使抛出异常,我们也需要处理此清理代码,这会给事情的方案中带来另一个麻烦。但是,在 c++ 中,我们也有 RAII,这意味着析构函数是要走的路。
这里有一个我喜欢的技巧,可以避免goto:
bool success = false;
do {
r = do_something();
if (r) break;
r = do_something_else();
if (r) break;
r = do_something_again();
if (r) break;
success = true;
} while(0);
if (! success) {
clean_up();
}
对于给定的代码,您可以在它们上使用以下许多变体中的任何一种:
int myfunc()
{
blah a;
blah2 b;
blah3 c;
blah4 d;
blah5 e;
int iRes;
if ((iRes = DoSomething1(a, b, c)) > 0 ||
(iRes = DoSomething2(a, c, e)) > 0 ||
...
(iRes = DoSomething10(c, d, e)) > 0)
{
clean1(a, b, c);
clean2(d, e);
log_error();
return 1;
}
clean1(a, b, c);
clean2(d, e);
return 0;
}
如果在clean1()
和clean2()
之前还是之后调用log_error()
都没有关系,则可以使用:
int myfunc()
{
blah a;
blah2 b;
blah3 c;
blah4 d;
blah5 e;
int iRes;
if ((iRes = DoSomething1(a, b, c)) > 0 ||
(iRes = DoSomething2(a, c, e)) > 0 ||
...
(iRes = DoSomething10(c, d, e)) > 0)
{
log_error();
}
clean1(a, b, c);
clean2(d, e);
return 0;
}
甚至:
int myfunc()
{
blah a;
blah2 b;
blah3 c;
blah4 d;
blah5 e;
int iRes;
if ((iRes = DoSomething1(a, b, c)) > 0 ||
(iRes = DoSomething2(a, c, e)) > 0 ||
...
(iRes = DoSomething10(c, d, e)) > 0)
{
// Don't need to do anything here
}
clean1(a, b, c);
clean2(d, e);
if (iRes > 0)
log_error();
return 0;
}
if
的正文可能是最终作业:
int myfunc()
{
blah a;
blah2 b;
blah3 c;
blah4 d;
blah5 e;
int iRes;
if ((iRes = DoSomething1(a, b, c)) > 0 ||
(iRes = DoSomething2(a, c, e)) > 0 ||
...
)
{
iRes = DoSomething10(c, d, e);
}
clean1(a, b, c);
clean2(d, e);
if (iRes > 0)
log_error();
return 0;
}
这种重构取决于清理在所有情况下都是相同的。 通常,它不是那么系统。 由于您没有显示任何a
、b
、c
、d
或e
正在初始化,因此很难确定什么是真正安全的。 如果doSomethingN()
函数C++并通过引用获取参数,那么doSomething1()
可以初始化a
、b
、c
——但对clean2()
的调用可能发生在d
或e
有值之前。
相关文章:
- 如果没有malloc,链表实现将失败
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 如果我只是不访问queue_front节点的子节点,而是将它们推到队列中呢?还是BFS吗
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 设计一个只能由特定类实例化的类(如果可能的话,通过make_unique)
- 文件模式标志"ios::app"是否用于删除文件(如果文件已存在)?
- 避免"如果清理失败"重复的模式
- 在发布模式下崩溃,但如果可调试为 true - 不是..什么是可能的问题
- 如果 Proactor 设计模式优于异步 I/O,为什么它在 ASIO 中不是默认值?
- 如果复合模式中的每个复合项都有其自己的许多独特属性,是否可以
- C++可变参数模板函数调用应用了什么模式匹配(如果有)
- 如果命令不只是打印结果,而是进入某种交互模式,如何处理来自C++的终端命令执行的输出?
- 如果我在DFS方法中生成频繁模式,如何构建DFS树
- 如果从文件流中读取数据,gSOAP为什么将stdin模式设置为二进制
- 代码:块调试模式:我的代码崩溃,如果构建和运行,但如果调试/继续
- Clang定义了什么宏来宣布c++ 11模式(如果有的话)?
- 如果我为多个编写器使用额外的互斥锁,我可以在 SQLite3 中使用 WAL 模式吗?
- 如果字符位于引号之间,则不匹配(AKA具有编程字符串模式)