传统C/C++几种错误处理的方式
错误码
C语言强烈依赖于错误码来处理错误,包括以下几类:
- 返回值
- 输出参数
- 错误单例(errno)
C语言的错误码通常是一个int类型,保持着底层的特性,没有任何的类型安全措施。
异常
C++继承了错误码的方式(因为它有许多调用C函数的地方),同时它也提出了新的错误传播方式-异常。
使用异常来处理错误有许多优势,因此它也被标准库所采用。
尽管异常有许多优势,错误码依然在广泛使用,包括在标准库当中。其中一个原因是考虑到和C语言的二进制兼容,但许多新的库依然更倾向于使用错误码而不是异常,有以下原因:
- 使用异常,很难解释程序的控制流
- 异常比错误码有更大的开销
第一个原因导致即使看起来只有一个return的函数,也可能因为异常有很多返回点,导致代码很难review,也很难被静态工具检查。
第二个原因在安全相关的系统中更关键,事实上,通过了ASIL认证的编译器都不支持异常,一个实际的例子是,C++的异常在处理时,使用了动态内存的分配,这个分配不可预测,并且时间不确定。
这些原因使得异常不适合汽车安全相关的系统。
不成功操作的类型
在实现AP的API过程中,不同的异常情况需要被检测到或者被报告,根据它们的特点,分为如下几种:
- 错误:错误是假定bug-free的函数无法实现其功能,它可能是由于收到了无效输入,或非预期输入等,错误是可恢复的
- 背离:背离是指应用程序框架的前置或后置条件失败的结果,AP中背离是对平台失败的断言,背离是无法恢复的
- 损坏:损坏是指系统资源的损坏,比如栈溢出,内存错误,损坏是不可恢复的
默认分配失败:指的是框架的默认内存分配机制无法满足分配,是不可恢复的
AP中不成功操作的处理
对于AP中不成功的操作,处理方式不同:
对于错误的处理:函数应返回
ara::core::Result
或ara::core::Future
来处理错误- 对于背离的处理:背离有两种处理方式:
- 抛出不是
ara::core::Exception
子类的异常 - 调用
ara::core::Abort
终止进程
- 抛出不是
- 对于损坏的处理:如果检测到损坏,应当导致不成功的进程终止,实现方式自定义
- 对于默认分配失败得处理:和背离一样
错误处理的设施
ErrorCode
就像名字所展示,ara::core::ErrorCode
是错误码的一种形式,但是它是一个类类型(而不是传统的int),模仿的是std::error_code
。与典型的C API使用的错误码相比,能处理更复杂的错误。
它包含一个底层的错误值以及到错误域的引用。
错误值是一个枚举类型,通常是带范围的。当存储在ara::core::ErrorCode
中时,它的类型擦除到一个整型中,因此处理方式类似于C风格的错误值。错误域的引用定义了这个错误值的内容适用范围,提供了一些类型安全的机制。
一个ara::core::ErrorCode
也包含一个支持数据值,可以添加更多的数据到这个错误。ara::core::ErrorCode
实例通常不会直接创建,而是通过ara::core::Result::FromError
转发。
一个ara::core::ErrorCode
不局限于任何已知的错误域,它内部的类型擦除保证它很简单(非模板化),可以包含任意域的任意错误。
然而,比较两个ara::core::ErrorCode
实例只考虑错误值和错误域,支持数据值不检查是否相等,因为典型的判断场景是这样的: ```cpp ErrorCode ec = … if (ec == MyEnum::some_error) // … else if (ec == AnotherEnum::another_error) // …
``` 这些判断都会在右操作数建一个临时对象,不包含有意义的支持数据值
ErrorDomain
ara::core::ErrorDomain
是一个抽象基类,它的派生类是定义在各FC或者APP内的特定错误域,这个类类似于std::error_category,但有不同的签名。
一个错误域有相关联的错误码枚举和相关的基础异常类型,它们通常都在同一个namespace内.ara::core::ErrorDomain
有一个uint64_t类型的ID,这个ID必须是唯一的,就代表了这个错误域,在跨ECU进行处理时,通过ID来判断错误域。
Result
ara::core::Result
遵循了ValueOrError
的概念,它要么包含一个值(ValueType类型),要么包含一个错误(ErrorType类型),ValueType和ErrorType都是ara::core::Result
的模板参数,可以是任意类型,但ErrorType默认是ara::core::ErrorCode
,并且希望贯穿于整个AP平台。ara::core::Result
表现为连接适用无异常API和有异常时ara::core::ErrorCode
的包装类型,因为ara::core::ErrorCode
和错误域特定的异常类型有直接的映射关系,ara::core::Result
可以通过ValueOrThrow
来抛出异常。
Future和Promise
ara::core::Future
和ara::core::Promise
很像std::future
和std::promise
,但是已经处理过ara::core::Reuslt
的互操作。ara::core::Future
也是要么包含一个值,要么包含一个错误(即使是在ready状态)。ara::core::Promise
增加了一个SetError
接口,来设置错误,相对应的,ara::core::Future
也增加了新的函数GetResult
,它和get
很像,但不会抛出异常,而是返回一个ara::core::Result
因此ara::core::Future
也有两种方式来处理错误,一种是通过异常,另一种是通过Result。ara::core::Result
用来处理同步函数调用的错误,而ara::core::Future
用来处理异步调用