使用std::error_code的错误堆栈

3
对于错误处理,对我来说异常不太适用,因为我的代码将是一个动态链接库。此外,我认为异常只应在特殊情况下使用。但是会出现一些错误并非特殊情况。另一个问题是我的库将从C#中调用。因此,在所有错误都使用异常似乎不是正确的选择。
但是我发现std::error_code和std::error_category的概念非常好,并希望在我的应用程序中使用它。然而,我也想为错误提供某种堆栈跟踪。
考虑以下例子: 用户想从数据库中加载域对象。为了加载这个域对象,应用程序需要从不同的表中加载行。假设找不到所需的行之一。在这种情况下,数据库层将生成一些“未找到”错误。如果将此错误传播到用户,错误消息将不会很有用,因为没有人知道未找到的是什么。同样,如果每个层处理下层的错误并生成相应的新错误,抽象底层错误,我最终会得到类似于“无法从数据库加载”的东西,这再次不是很有用。我希望两者兼备。即每个层抽象它从任何较低级别接收到的错误,以能够向最终用户显示描述性消息,但同时我不想失去有关较低级别错误的信息。 因此,我希望有类似于错误跟踪的东西。
我考虑从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类是不可能的。因此,现在我看不到任何不需要在堆上进行分配的实现方式。

顺便说一下,在我看来,std::error_codestd::error_category并不适合你的情况。应用程序应该使用异常来通知错误,错误代码是非C++环境(例如操作系统API或设备驱动程序)的遗产。在这种情况下,我认为你应该创建一个包装器,将它们的错误代码封装在std::error_code中,并使用类似于C++的接口公开它们,然后使用异常在应用程序中管理错误(使用自己的异常和error_code()成员获取低级错误代码(如果有)。 - Adriano Repetti
我无法使用异常,因为我正在编写的代码应该是一个动态链接库。 - sigy
但这正是我的问题。这样做是否安全(就效率而言)?我是否会通过添加这些成员和方法破坏对std::error_code进行的优化? - sigy
你可以始终将内部调用包装在使用try {...} catch()的函数中,以便在向用户返回任何内容之前处理内部异常。然后,您可以决定是否生成另一个更详细的异常、返回代码等。 - DNT
1
就像我所提到的,异常不是我的选择。 - sigy
显示剩余2条评论
1个回答

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;
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接