Active 错误代码






4.79/5 (10投票s)
一个确保错误代码得到检查而不是被忽略的机制。
动机
永恒的问题——是应该使用错误代码还是异常?两者都有各自的优势和劣势。异常不需要检查,但会使本地检查错误更加困难,并影响性能。
引入主动错误代码——错误代码返回的值会知道你是否已经检查过它们……如果你没有检查,那么它们就会抛出异常(如果它们指示了错误)。
教程
那么,什么是错误代码?对于主动错误代码而言,错误代码是一种任意类型的值,可以确定为“好”(不指示错误)或“坏”(指示错误)。例如,Win32 函数 CreateFile
在文件无法打开时返回预定义的 INVALID_HANDLE_VALUE
值,类型为 HANDLE
。另一个例子是 CoCreateInstance
,它返回一个 HRESULT
。如果最高有效位被设置,这表示某种形式的失败,所以没有一个单一的“好”或“坏”值。在这种情况下,你可能需要某种函数来确定错误代码的状态。
因此,主动错误代码分为两类——一种是具有单一值来指示已发生(或未发生)错误状态的错误,另一种是使用函数来确定其状态的错误。
// Define and use an error code (of type int) that has a single 'good' value (0)
typedef ErrCodeWithGoodValue<int, 0> Err;
Err Test_Err(bool isGood)
{
// Return the error's good value if isGood is true, some other value if false.
int e = isGood?(Err::value):(Err::value+1);
return Err(e);
}
// Define and use an error code (of type HRESULT) defined by a function (HRIsGood)
bool HRIsGood(HRESULT hr)
{
return SUCCEEDED(hr);
}
typedef ErrCodeWithFn<HRESULT, &HRIsGood> eHRESULT;
eHRESULT Test_eHRESULT(bool isGood)
{
return isGood?S_OK:E_UNEXPECTED;
}
这些错误代码值可以手动检查,也可以让它们抛出异常
void TestThrowOnError(bool b)
{
// Will throw an exception when Test's return value
// goes out of scope, i.e. at the end of the line
Test_eHRESULT(b);
}
void TestHandleError(bool b)
{
if (Test_Err(b).good())
{
std::cout << "Good\n";
}
else
{
std::cout << "Bad\n";
}
}
如果使用了错误代码的异常抛出能力,那么错误值返回就不应该赋给一个变量——这将导致错误值的检查延迟到变量超出作用域,这可能是在执行依赖于可能出错的函数的功能之后。
用法
这个库由一个单独的头文件组成,ActiveErrors.h。这个头文件应该被需要定义错误代码类型的源文件#include
。一般而言,这很可能会在声明函数签名(以及类定义内的)的头文件中。
参考
template <class ErrCodeType, bool (*IsGoodValueFn)(ErrCodeType)> class ErrCodeWithFn
模板类,它使用一个函数来确定错误值是否指示了一个错误条件。
模板参数
class ErrCodeType
ErrCodeType
应该是一个可复制构造的类型。此外,由于该类型的值是通过值传递的,因此它应该是一个易于复制的类型。
bool (*IsGoodValueFn)(ErrCodeType)
指向用于确定错误代码好坏的函数的指针。请注意,这不能是成员函数的指针。
方法
ErrCodeWithFn(ErrCodeType errorCode)
ErrCodeWithFn(ErrCodeWithFn const& other)
ErrCodeWithFn& operator=(ErrCodeWithFn const& other)
构造函数存储错误代码值并将其标记为未检查。复制构造函数和赋值运算符将源错误对象标记为已检查。
~ErrCodeWithFn()
析构函数将在其值指示错误且尚未检查时抛出异常。
bool good() const
good()
指示是否发生了错误。执行此方法会将错误代码标记为已检查。
operator ErrCodeType() const
此转换运算符允许检查错误代码的特定值。执行此方法会将错误代码标记为已检查。
template <class ErrCodeType, ErrCodeType GoodValue>
class ErrCodeWithGoodValue
模板类,对于错误代码类型,它有一个单一的“好”值。
模板参数
class ErrCodeType
ErrCodeType
应该是一个整数类型。这对 ErrCodeWithFn
来说是一个更严格的约束,因为该类型的一个项被用作模板参数。
ErrCodeType GoodValue
类型为 ErrCodeType
的值,这是该类型 ErrCodeType
的唯一“好”值。
方法
ErrCodeWithGoodValue(ErrCodeType errorCode)
ErrCodeWithGoodValue(ErrCodeWithGoodValue const& other)
ErrCodeWithGoodValue& operator=(ErrCodeWithGoodValue const& other)
构造函数存储错误代码值并将其标记为未检查。复制构造函数和赋值运算符将源错误对象标记为已检查。
~ErrCodeWithGoodValue()
析构函数将在其值指示错误且尚未检查时抛出异常。
bool good() const
good()
指示是否发生了错误。执行此方法会将错误代码标记为已检查。
operator ErrCodeType() const
此转换运算符允许检查错误代码的特定值。执行此方法会将错误代码标记为已检查。
template <class ErrCodeType, ErrCodeType BadValue> class ErrCodeWithBadValue
模板类,对于错误代码类型,它有一个单一的“坏”值。
模板参数
class ErrCodeType
ErrCodeType
应该是一个整数类型。这对 ErrCodeWithFn
来说是一个更严格的约束,因为该类型的一个项被用作模板参数。
ErrCodeType BadValue
类型为 ErrCodeType
的值,这是该类型 ErrCodeType
的唯一“坏”值。
方法
ErrCodeWithBadValue(ErrCodeType errorCode)
ErrCodeWithBadValue(ErrCodeWithBadValue const& other)
ErrCodeWithBadValue& operator=(ErrCodeWithBadValue const& other)
构造函数存储错误代码值并将其标记为未检查。复制构造函数和赋值运算符将源错误对象标记为已检查。
~ErrCodeWithBadValue()
析构函数将在其值指示错误且尚未检查时抛出异常。
bool good() const
good()
指示是否发生了错误。执行此方法会将错误代码标记为已检查。
operator ErrCodeType() const
此转换运算符允许检查错误代码的特定值。
与其他错误代码类不同,此运算符将在值是“坏”的且在调用此运算符之前尚未检查其有效性时抛出异常。这是因为这种类型的返回值很可能返回一些可供您使用的资源,因此在检查值后再使用它比其他错误代码类型更容易发生。
这种资源的一个例子是从内存分配例程返回的指针,该指针在不是 null
时是“好”的。
class UncheckedErrorBase
所有当未检查的“坏”错误类被销毁时抛出的异常的基类。
template <class ErrCodeType> class UncheckedError;
模板类;当未检查的“坏”错误类被销毁时抛出的异常。此类派生自 UncheckedErrorBase
,以便任何未检查的错误都可以用单个 catch
处理程序捕获。
模板参数
class ErrCodeType
底层错误值的类型。
历史
- 2009年5月1日:初始版本