【问题标题】:Understanding the <system_error> facility in C++11了解 C++11 中的 <system_error> 工具
【发布时间】:2014-03-16 07:29:00
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: c++ c++11 error-handling error-code std-system-error


    【解决方案1】:

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

    19.5 [syserr] 定义了一长串标准(和可移植)错误条件(例如 errc::no_such_file_or_directory),这些条件与 errno 的特定值(例如 ENOENT)显式关联。因此,您不需要知道系统上生成的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::errc 被标记为 is_error_condition_enum

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

    【讨论】:

      猜你喜欢
      • 2013-09-08
      • 2012-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多