std::Error_code的错误堆栈

Error stack with std::error_code

本文关键字:错误 堆栈 Error std code      更新时间:2023-10-16

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

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

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

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

我们希望error_code是一种可以在不进行切片和不需要堆分配的情况下复制的值类型,但我们也希望它具有基于错误类别的多态行为。

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

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

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

最后我从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;
};