【问题标题】:Error stack with std::error_code带有 std::error_code 的错误堆栈
【发布时间】:2014-02-11 12:17:02
【问题描述】:

对于错误处理,异常对我来说是个问题,因为我的代码将是一个动态链接库。此外,我认为异常只应在特殊情况下使用。但是我会遇到一些可能发生错误的情况,但并不例外。另一个问题是我的库将从 C# 调用。所以在这里对所有错误使用异常似乎不是正确的选择。

但我发现 std::error_code 和 std::error_category 的概念非常令人愉快,并希望在我的应用程序中使用它。不过,我还想提供某种堆栈跟踪以发现错误。

考虑一个例子: 用户想要从数据库加载域对象。要加载此域对象,应用程序需要从不同的表中加载行。假设找不到所需的行之一。在这种情况下,数据库层会产生一些 "not found" 错误。如果我将此错误全部传播给用户,则错误消息将不会很有帮助,因为没有人知道 what 没有找到。同样,如果每一层都处理下层的错误并生成相应的新错误,抽象出低级错误,我最终会得到类似“无法从数据库加载”之类的东西,这又不是很有帮助。我想要的是两者兼得。也就是说,每一层都抽象了它从任何较低级别获得的错误,以便能够向最终用户显示描述性消息,但同时我不想丢失有关低级别错误的信息。 所以我想要一个错误堆栈跟踪之类的东西。

我考虑从 std::error_code 派生并使用指向底层 std::error_code 的指针和获取所有这些底层对象的方法来扩展类。但是,我不确定这种技术是否是一个好主意,因为我读到在设计 std::error_code 以使其高效时已非常小心。

我们希望 error_code 是一种无需切片且无需堆分配即可复制的值类型,但我们还希望它具有基于错误类别的多态行为。

编辑 我现在认为这种技术也会引入切片问题,不是吗?

编辑 2 我现在想通过从 std::error_code 派生来实现它。我的派生类将有一个 boost::optional ,而不是一个指针,什么地方需要堆分配。这样,内部错误代码可以简单地通过复制在堆栈上创建。 boost::optional 可以正确表示不存在的内部错误代码。切片仍然是一个问题,但我想这是可以忽略的,因为将派生类的实例分配给 std::error_code 变量的情况是不必要的,即使发生这种情况,我只会丢失有关内部错误代码的信息。此外,我可以提供从 std::error_code 到没有内部错误代码的派生类的转换。

编辑 3 我没有想到不可能有一个包含 boost::optional 的类。所以现在我看不到在堆上没有分配的情况下我想要的任何可能性。

【问题讨论】:

  • 为什么不用inner_exception() 方法定义自己的std::exception 派生异常类来返回内部异常(源自较低级别)?您的 UI 甚至可能会捕捉到 business_exception,但通过 inner_exception() 导航,您可能会记录每一层中发生的所有事情(好吧,如果您想这样做,可能出于安全原因不要公开基本细节)。
  • 哦,顺便说一句,IMO std::error_codestd::error_category 不适合您的场景。应用程序应该使用异常来通知错误,错误代码是非 C++ 环境的遗产(例如操作系统 API 或设备驱动程序)。在这种情况下,IMO 您应该围绕该函数创建一个包装器,用std::error_code 封装其错误代码;使用 C++ish 接口公开它们,然后使用异常管理应用程序中的错误(使用您自己的异常,使用 error_code() 成员获取低级错误代码(如果有)。
  • 我不能使用异常,因为我写的代码应该是动态链接库
  • 如果所有涉及的模块都是 C++ 并且它们使用相同的编译器,则可以使用它们。如果没有,那么即使std::error_code 也不起作用(你必须回到int)。如果出于任何原因您想依赖错误代码(但它们应该仅用于兼容性,IMO),那么您可以应用相同的技术(error_code 是非密封类)
  • 正如我所提到的,例外不是我的选择。

标签: c++ c++11 error-handling


【解决方案1】:

最后我来自std::error_code。我的派生类有一个成员,它是指向同一类的实例的指针。我向该类添加了一个wrap() 方法,该方法将同一类的实例作为参数并在堆上分配它的副本。我的派生类的析构函数确保再次释放内存。我也为这个内部错误代码添加了一个 getter 方法。这样我可以堆叠几个错误代码。缺点是,我需要堆分配,但我只是希望,在我的场景中这不会导致严重的性能问题。 我的班级还提供从std::error_code 的转换。


class my_error : public std::error_code
{
public:
    my_error() : std::error_code(), m_innerError(NULL) {};
    my_error( int val, const std::error_category & cat ) : std::error_code(val, cat), m_innerError(NULL) {};
    my_error( std::error_code & error ) : std::error_code(error), m_innerError(NULL) {};
    my_error( const std::error_code & error ) : std::error_code(error), m_innerError(NULL) {};
    ~my_error()
    {
        delete m_innerError;
    }

    template <class ErrorCodeEnum>
    my_error(ErrorCodeEnum e,
                   typename boost::enable_if<std::is_error_code_enum<ErrorCodeEnum> >::type* = 0)
    {
        *this = make_custom_error(e);
    }

    template<typename ErrorCodeEnum>
    typename boost::enable_if<std::is_error_code_enum<ErrorCodeEnum>, error_code>::type &
    operator=( ErrorCodeEnum val )
    {
        *this = make_custom_error(val);
        return *this;
    }

    my_error const * get_inner() const
    {
        return m_innerError;
    };

    void wrap( const my_error & error)
    {
        m_innerError = new my_error(error);
    };

private:
    my_error * m_innerError;
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-04
    • 2011-03-03
    • 2015-11-20
    • 2016-05-16
    • 2011-03-27
    • 2017-08-03
    • 1970-01-01
    • 2020-11-14
    相关资源
    最近更新 更多