设计异常类


9

我正在编写一个小型库,在设计异常处理时遇到了一些麻烦。我必须说,我(仍然)对C ++语言的此功能感到困惑,并且我尝试尽可能多地阅读该主题,以了解如何正确使用异常类将要做什么。

我决定使用一种system_error方法,该方法从future_error该类的STL实现中获得启发。

我有一个包含错误代码的枚举:

enum class my_errc : int
{
    error_x = 100,
    error_z = 101,
    error_y = 102
};

和一个异常类(由error_category结构类型和system_error模型所需的所有其他内容支持):

// error category implementation
class my_error_category_impl : public std::error_category
{
    const char* name () const noexcept override
    {
        return "my_lib";
    }

    std::string  message (int ec) const override
    {
        std::string msg;
        switch (my_errc(ec))
        {
        case my_errc::error_x:
            msg = "Failed 1.";
            break;
        case my_errc::error_z:
            msg = "Failed 2.";
            break;
        case my_errc::error_y:
            msg = "Failed 3.";
            break;
        default:
            msg = "unknown.";
        }

        return msg;
    }

    std::error_condition default_error_condition (int ec) const noexcept override
    {
        return std::error_condition(ec, *this);
    }
};

// unique instance of the error category
struct my_category
{
    static const std::error_category& instance () noexcept
    {
        static my_error_category_impl category;
        return category;
    }
};

// overload for error code creation
inline std::error_code make_error_code (my_errc ec) noexcept
{
    return std::error_code(static_cast<int>(ec), my_category::instance());
}

// overload for error condition creation
inline std::error_condition make_error_condition (my_errc ec) noexcept
{
    return std::error_condition(static_cast<int>(ec), my_category::instance());
}

/**
 * Exception type thrown by the lib.
 */
class my_error : public virtual std::runtime_error
{
public:
    explicit my_error (my_errc ec) noexcept :
        std::runtime_error("my_namespace ")
        , internal_code(make_error_code(ec))
    { }

    const char* what () const noexcept override
    {
        return internal_code.message().c_str();
    }

    std::error_code code () const noexcept
    {
        return internal_code;
    }

private:
    std::error_code internal_code;
};

// specialization for error code enumerations
// must be done in the std namespace

    namespace std
    {
    template <>
    struct is_error_code_enum<my_errc> : public true_type { };
    }

在少数情况下,我抛出由错误代码枚举说明的异常。

上面的内容与我的一位审阅者不太合。他认为,我应该使用派生自std::runtime_error其的基类来创建异常类的层次结构,因为将错误代码嵌入条件中会混合各种东西(异常和错误代码),并且处理一个问题将更加繁琐处理;异常层次结构还可以轻松自定义错误消息。

我的论据之一是,我想保持简单,我的库不需要抛出多种类型的异常,并且在这种情况下,自定义也很容易,因为它是自动处理的- error_code具有error_category与之关联的翻译编码为正确的错误消息。

我不得不说我没有很好地捍卫自己的选择,这证明了我对C ++异常还有一些误解。

我想知道我的设计是否合理。我不得不承认我也看不到另一种方法比我选择的方法有什么优势?我该怎么做才能改善?


2
我倾向于原则​​上同意您的审阅者的意见(将错误代码和异常混在一起实际上不是那么有用)。但是,除非您拥有一个庞大的库,且具有较大的层次结构,否则它也没有用。包含消息字符串的基本异常,仅当异常的捕获程序可以潜在地使用异常的唯一性来解决问题时,才具有单独的异常。
马丁·约克

Answers:


9

我认为您的同事是对的:您正在根据在层次结构中实现的简单程度来设计异常案例,而不是基于客户端代码的异常处理需求。

使用一种异常类型和一个错误条件枚举(您的解决方案),如果客户端代码需要处理单个错误情况(例如my_errc::error_x),则他们必须编写如下代码:

try {
    your_library.exception_thowing_function();
} catch(const my_error& err) {
    switch(err.code()) { // this could also be an if
    case my_errc::error_x:
        // handle error here
        break;
    default:
        throw; // we are not interested in other errors
    }
}

使用多种异常类型(在整个层次结构中具有相同的基础),您可以编写:

try {
    your_library.exception_thowing_function();
} catch(const my_error_x& err) {
    // handle error here
}

异常类如下所示:

// base class for all exceptions in your library
class my_error: public std::runtime_error { ... };

// error x: corresponding to my_errc::error_x condition in your code
class my_error_x: public my_error { ... };

编写库时,重点应放在易用性上,而不是(有必要)简化内部实现。

仅当禁止在库中正确执行代码时,才应牺牲易用性(客户端代码的外观)。


0

我同意您的评论者和@utnapistim。system_error当某些错误需要特殊处理时,您可以在实现跨平台事物时使用方法。但是即使在这种情况下,这也不是一个好的解决方案,而是更少的邪恶解决方案。

还有一件事。创建异常层次结构时,不要使其变得很深。仅创建那些可由客户端处理的异常类。在大多数情况下,我只使用std::runtime_errorstd::logic_errorstd::runtime_error当出现问题时我抛出错误,并且我无能为力(用户从计算机弹出设备,他忘记了该应用程序仍在运行),并且std::logic_error程序逻辑中断(用户试图从不存在的数据库中删除记录,但是在删除操作之前,他抛出该错误)可以检查它,因此他得到逻辑错误)。

作为图书馆开发人员,请考虑您的用户需求。尝试自己使用它,然后思考,是否会让您感到舒适。比起代码示例,您可以向审稿人解释您的立场。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.