C++核心准则E.27:如果无法抛出异常,系统化运用错误处理代码
E.27: If you can't throw exceptions, use error codes systematically
E.27:如果无法抛出异常,系统化运用错误处理代码
Reason(原因)
Systematic use of any error-handling strategy minimizes the chance of forgetting to handle an error.
系统化运用任何错误处理策略都可以减少忘记处理某个错误的可能性。
See also: Simulating RAII
Note(注意)
There are several issues to be addressed:
有多个课题需要说明:
How do you transmit an error indicator from out of a function?
如何在函数外部发送错误指示器?
How do you release all resources from a function before doing an error exit?
如何在执行发生错误从函数退出之前释放所有资源?
What do you use as an error indicator?
使用什么作为错误指示器?
In general, returning an error indicator implies returning two values: The result and an error indicator. The error indicator can be part of the object, e.g. an object can have a valid() indicator or a pair of values can be returned.
一般情况下,返回错误指示器包含包含两个值:结果和错误指示器。错误指示器可以是对象的一部分,例如对象可以包含一个valid()检查函数或者一对可以返回的值。
Example(示例)
Gadget make_gadget(int n)
{
// ...
}
void user()
{
Gadget g = make_gadget(17);
if (!g.valid()) {
// error handling
}
// ...
}
This approach fits with simulated RAII resource management.
这个做法符合模拟RAII的资源管理。
The valid() function could return an error_indicator (e.g. a member of an error_indicator enumeration).
valid()函数可以返回error_indicator(例如,枚举类型error_indicator的某个成员。
Example(示例)
What if we cannot or do not want to modify the Gadget type? In that case, we must return a pair of values. For example:
如果我们不能或不想修改Gadget类型时该怎么办呢?这种情况,我们必须返回值对。例如:
std::pair
make_gadget(int n) {
// ...
}
void user()
{
auto r = make_gadget(17);
if (!r.second) {
// error handling
}
Gadget& g = r.first;
// ...
}
As shown, std::pair is a possible return type. Some people prefer a specific type. For example:
如代码所示,std::pair是可能的返回值类型。有些人更愿意使用特殊类型。例如:
Gval make_gadget(int n)
{
// ...
}
void user()
{
auto r = make_gadget(17);
if (!r.err) {
// error handling
}
Gadget& g = r.val;
// ...
}
One reason to prefer a specific return type is to have names for its members, rather than the somewhat cryptic first and second and to avoid confusion with other uses of std::pair.
更愿意使用特殊返回值类型的一个原因是可以为成员命名,而不是有些难以理解的first和second,另外的好处就是可以和使用std::pair的其他代码混淆。
Example(示例)
In general, you must clean up before an error exit. This can be messy:
一般情况下,你必须在错误退出之前执行清理动作。这可能产生凌乱的代码:
std::pair
user() {
Gadget g1 = make_gadget(17);
if (!g1.valid()) {
return {0, g1_error};
}
Gadget g2 = make_gadget(31);
if (!g2.valid()) {
cleanup(g1);
return {0, g2_error};
}
// ...
if (all_foobar(g1, g2)) {
cleanup(g2);
cleanup(g1);
return {0, foobar_error};
}
// ...
cleanup(g2);
cleanup(g1);
return {res, 0};
}
Simulating RAII can be non-trivial, especially in functions with multiple resources and multiple possible errors. A not uncommon technique is to gather cleanup at the end of the function to avoid repetition (note the extra scope around g2 is undesirable but necessary to make the goto version compile):
模拟RAII可能需要特别处理,特别是包含多个资源和多个错误的时候。一个并不罕见的技术是将清除动作集中在函数末尾以避免重复(注意包含g2的额外作用域本来是不需要的,只是为了让goto版本代码通过编译)
std::pair
user() {
error_indicator err = 0;
int res = 0;
Gadget g1 = make_gadget(17);
if (!g1.valid()) {
err = g1_error;
goto g1_exit;
}
{
Gadget g2 = make_gadget(31);
if (!g2.valid()) {
err = g2_error;
goto g2_exit;
}
if (all_foobar(g1, g2)) {
err = foobar_error;
goto g2_exit;
}
...
g2_exit:
if (g2.valid()) cleanup(g2);
}
g1_exit:
if (g1.valid()) cleanup(g1);
return {res, err};
}
The larger the function, the more tempting this technique becomes. finally can ease the pain a bit. Also, the larger the program becomes the harder it is to apply an error-indicator-based error-handling strategy systematically.
函数越大,使用这类技术的诱惑越大。finally可以稍微减轻痛苦。同时,问题越大,基于错误指示器的系统化错误处理策略就越难运用。
We prefer exception-based error handling and recommend keeping functions short.
我们比较喜欢基于异常的错误处理并且推荐保持函数短小。
See also: Discussion
参见:问题讨论
See also: Returning multiple values
Enforcement(实施建议)
Awkward.
不容易。
原文链接
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#e26-if-you-cant-throw-exceptions-consider-failing-fast
新书介绍
以下是本人3月份出版的新书,拜托多多关注!
本书利用Python 的标准GUI 工具包tkinter,通过可执行的示例对23 个设计模式逐个进行说明。这样一方面可以使读者了解真实的软件开发工作中每个设计模式的运用场景和想要解决的问题;另一方面通过对这些问题的解决过程进行说明,让读者明白在编写代码时如何判断使用设计模式的利弊,并合理运用设计模式。
对设计模式感兴趣而且希望随学随用的读者通过本书可以快速跨越从理解到运用的门槛;希望学习Python GUI 编程的读者可以将本书中的示例作为设计和开发的参考;使用Python 语言进行图像分析、数据处理工作的读者可以直接以本书中的示例为基础,迅速构建自己的系统架构。
觉得本文有帮助?请分享给更多人。
关注微信公众号【面向对象思考】轻松学习每一天!
面向对象开发,面向对象思考!