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<Gadget, error_indicator> 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<int, error_indicator> 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<int, error_indicator> 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
- spring-boot 速成(6) 整合disconf
- 安卓第四夜 概念漫游(下)
- spring-boot 速成(7) 集成dubbo
- spring-boot 速成(8) 集成druid+mybatis
- Java常用类(一)之Object类详解
- .NET魔法堂:工程构建基石->MSBuild
- 微信小程序能干哪些事,有什么优势?
- 如何告诉手机我是“我”呢?
- 没有任何类型 Windows 的外层实例可访问---Java内部类与外类型
- Hadoop(十二)MapReduce概述
- 安卓第一夜 第一个应用
- spring cloud 学习(1) - 基本的SOA示例
- SVN冲突
- 什么叫微信小程序分销系统?如何通过分销系统来实现你的创业梦
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 20201001_kpay支付项目搭建
- PGSQL分组查询报错
- ConcurrentHashMap#概述
- var、let、const的区别
- IDEA 破解到2099年(202004亲测)
- Flutter基础widgets教程-Chip篇
- 6.超链接-HTML基础
- lombok 表达式的相关特性
- 写给前端程序员的英文学习指南
- 浅谈spring aop的实现原理和实现过程
- Cypress系列(60)- 运行时的截图和录屏
- 请停止使用Excel进行数据分析,升级到Python吧
- git版本控制器的相关操作
- 离线 Android Studio 环境
- Actor:人生如戏全靠演技--“三维度”逻辑编程语言的设计(3) 角色是一种特定的关系名不正言不顺角色才是真正的主人演员的天分再好的演技也需要角色好演员更需要好剧本