简体   繁体   中英

Error stack with std::error_code

For error handling, exceptions are problematic for me because my code is going to be a dynamically linked library. Furthermore I think exceptions should only be used in exceptional cases. But I will have cases where an error might occur that is not exceptional. Another problem is that my library will be called from C#. So using exceptions for all errors does not seem the right choice here.

But I find the concept of std::error_code and std::error_category quite pleasant and would like to use it in my application. However, I would also like to offer some kind of stack trace for errors.

Consider an example: The user wants to load a domain object from a database. To load this domain object the application needs to load rows from different tables. Suppose one of the required rows cannot be found. In this case the database layer would generate some "not found" error. If i propagate this error all up to the user the error message will not be very helpful because nobody knows what was not found. Likewise, if each layer handles the errors of the lower layer and generates a corresponding new error, abstracting the low level error, I would end up with something like "unable to load from database" which again is not very helpful. What I would like is to have both. That is each layer abstracts errors it gets from any lower level to be able to display descriptive messages to the end user but at the same time I don't want to lose information about the low level error. So I would like to have something like an error stack trace.

I thought about deriving from std::error_code and extend the class with a pointer to an underlying std::error_code and methods to get all these underlying objects. However, I am not sure if this technique would be a good idea because I read that care was taken when designing std::error_code to make it efficient.

We want error_code to be a value type that can be copied without slicing and without requiring heap allocation, but we also want it to have polymorphic behavior based on the error category.

EDIT I now think this technique would also introduce slicing problems, wouldn't it?

EDIT 2 I now think to implement it by deriving from std::error_code. Instead of a pointer, what would need heap allocation somewhere, my derived class would have a boost::optional. This way the inner error code could be created on the stack simply by copying it. A non-existing inner error code could be represented by boost::optional correctly. Slicing would still be a problem but I guess it is neglectable because the case of assigning an instance of my derived class to a std::error_code variable would not be necessary and even if it happens I would only loose information about the inner error codes. Furthermore I could provide a conversion from std::error_code to my derived class that has no inner error codes.

EDIT 3 I didn't think of that it is not possible to have a class containing a boost::optional of itself. So right now I don't see any possibility for what I would like to have without an allocation on the heap.

Finally I'm deriving from std::error_code . My derived class has a member that is a pointer to an instance of the same class. I added a wrap() method to that class that takes an instance of the same class as argument and allocates a copy of it on the heap. The destructor of my derived class ensures that the memory is freed again. I added a getter method for this inner error code as well. This way i can stack several error codes. Drawback is, that I need heap allocation, but I simply hope, that in my scenario this will not cause significant performance issues. My class also provides conversion from 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;
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM