了解 <system_error> C++11 中的设施

Understanding the <system_error> facility in C++11

本文关键字:C++11 gt 设施 lt system 了解 error      更新时间:2023-10-16

我正在尝试使用system_error工具来处理我的库中的错误。我将简要讨论库的结构,以防您发现它有帮助:库的名称空间称为commons,在此名称空间下,我有另一个名称空间dynlibdynlib包含负责加载.so/.dll文件的类:

namespace commons {
namespace dynlib {
class DynLibLoader {
};
}
}

DynLibLoader中可能出现的错误为LibraryFailedToLoadLibraryFailedToUnloadSymbolNotFound。因此,我对处理错误的想法如下:我将在名称空间dynlib下添加一个名称空间error。然后,在该名称空间下,我将为std::error_codes定义一个枚举,为std::error_conditions定义一个enum。根据我的理解,std::error_codes必须对应于errno(Linux)或GetLastError(Win32)的值,而std::error_conditions必须对应于像LibraryFailedToLoadSymbolNotFound等的值。因此,以下是我的问题:

  • 我对std::error_codestd::error_condition的理解正确吗
  • 我应该如何知道errnoGetLastError()的所有可能值,以便在我的std::error_codes枚举下定义它们?如果Microsoft将来在API中添加额外的错误值会怎样?我是否必须回到源代码,并在std::error_codes的枚举下定义它们
  • 如果我们在另一个平台上,当发生错误时,无法找出确切的系统错误代码,该怎么办
  • 如果我想对整个commons命名空间使用相同的std::error_codes,而只为每个子命名空间定义不同的std::error_condition(如dynlib),该怎么办。这是个好做法吗?我会答应,因为这样可以避免重复代码。但这背后有陷阱吗
  • 目前,我正在为commons的每个子命名空间使用一个std::error_category。这是个好做法吗?你认为我应该以不同的方式使用std::error_category

主要区别在于std::error_condition是可移植的(独立于平台),而std::error_code是依赖于平台的。通常,低级平台相关代码生成error_codes,客户端代码将这些error_codes与平台无关的error_conditions进行比较。

19.5[syserr]定义了与errno(例如ENOENT)的特定值显式关联的标准(和可移植)错误条件(例如,errc::no_such_file_or_directory)的长列表。因此,您不需要知道系统上生成的errnoGetLastError()的可能值的完整列表。您只需要知道与代码相关的标准值。例如,您的库实现可能如下所示:

void MyLibraryClass::foo(std::error_code &ec)
{
// whatever platform dependent operation that might set errno
// possibly with alternative platform-dependent implementations
ec = make_error_code(errno);
}

然后,您的客户端代码将检查error_code是否与任何特定的error_condition:匹配

error_code ec;
myLibraryInstance.foo(ec);
if (!ec)
{
// success
}
else if (errc::no_such_file_or_directory == ec)
{
// no_such_file_or_directory
}
else
{
// unknown or unexpected error
}

在您的情况下,您可能会定义自己的错误枚举(只有一个枚举),并将其标记为error_conditions以启用自动转换:

namespace commons
{
namespace dynlib
{
enum class errc {LibraryFailedToLoad=1, LibraryFailedToUnload, SymbolNotFound};
}
}
namespace std
{
template<> struct is_error_condition_enum<commons::dynlib::errc> : true_type {};
}
// TODO: implement make_error_code and make_error_condition

然后,您可以将各种平台相关操作的结果转换为适当的error_condition(或者error_code,如果您愿意的话):

void DynLibLoader::open(std::error_code &ec)
{
// possibly implement the windows version here as well
if (NULL == dlopen(filename, flag))
{
ec = make_error_code(errc::LibraryFailedToLoad);
}
}

您的客户端代码会将错误代码与可能的错误条件进行比较,如上所述:

error_code ec;
dynLibLoader.open(ec);
if (!ec)
{
// success
}
else if (commons::dynlib::errc::LibraryFailedToLoad == ec)
{
// Library Failed To Load
}
else
{
// unknown or unexpected error
}

注意,枚举commons::dynlib::errc::LibraryFailedToLoad被自动转换为error_condition(使用所提供的make_error_condition方法),因为commons::dynlib::errcis_error_condition_enum标记。

error_category到名称空间的映射可能是个人偏好,但这似乎有点人为。在这种特定情况下,为dynlib名称空间指定一个类别确实是有意义的,但很容易找到将类别分布在多个名称空间中有意义的示例。在某些情况下,将所有不同的错误枚举都放在一个唯一的命名空间(例如commons::errors)中可能是有意义和实用的。