与std :: error_code的错误堆栈

时间:2014-02-11 12:17:03

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

对于错误处理,异常对我来说是有问题的,因为我的代码将是一个动态链接的库。此外,我认为例外情况只应在特殊情况下使用。但是我会遇到可能发生错误但不例外的情况。另一个问题是我的库将从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本身的类是不可能的。所以现在我没有看到没有在堆上分配我想要的东西。

1 个答案:

答案 0 :(得分: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;
};