我想捕获一个异常并将其捆绑在我自己的异常中并向上抛出

时间:2014-01-04 01:28:45

标签: c++ exception

我有一个管理资源的课程。它需要一个可以从路径中检索资源的Loader类。 Loader是一个抽象基类,所以任何人都可以创建一个新的加载器。

如果请求的资源不在缓存中,资源管理器将从加载器请求它。如果加载程序失败,资源管理器会抛出一个名为LoadingError的异常类。

我希望Loader类在失败时抛出异常。如果需要,我可以要求他们从我自己的异常基类继承。

问题是,当抛出LoadingError时,我希望它包含有关失败原因的具体信息。但我不确切知道Loader会抛出什么异常。 (用户可能会在资源管理器周围的catch块中)。

要么我只能抛出一些一般信息的LoadingError,要么允许来自Loader的特定异常出来而不要触摸它。但我想捕获异常并将其捆绑在我自己的LoadingError异常中,或者在LoadingError中指向它。但我不知道指向异常将持续多长时间(关注坏指针)。

我应该做哪三个? (以及如何,对于第三个......)

由于

2 个答案:

答案 0 :(得分:8)

嵌套异常怎么样?

try { /* ... */ }
catch (...)
{
    throw MyException("An error occurred", std::current_exception());
}

只需创建一个存储异常的合适类:

struct MyException : std::exception
{
    std::string message;
    std::exception_ptr nested_exception;

    MyException(std::string m, std::exception_ptr e)
    : message(std::move(m))
    , nested_exception(std::move(e))
    { }

    // ...
};

捕获异常时,捕获器可以重新抛出嵌套异常:

try { /* load resource */ }
catch (MyException & e)
{
    log("Resource loading failed: " + e.what());
    std::rethrow_exception(e.nested_exception);
}

事实上,整个逻辑已由标准库通过std::throw_with_nested提供。

答案 1 :(得分:6)

当可以在整个代码中强制使用自己的异常类时,执行嵌套异常的最简单方法无疑是定义包含 {{1}的自定义异常类,可以通过 std::exception_ptr 获取, std::current_exception 标题。

但是,C ++ 11通过 <exception> 类以及 std::nested_exception 等函数支持嵌套异常。 std::throw_with_nested构造函数会选择? current_exception()`,如果有的话,并将其存储为嵌套异常。无法用expclitly指定嵌套异常:它始终是当前异常。

此机制还支持通过非异常感知代码(例如通过C回调)进行异常传播,并且它支持任意类的异常(和嵌套异常),而不仅仅是nested_exception和派生类。

所以,例如库代码使用此功能,然后希望能够处理此类标准嵌套异常,而不仅仅是一个自己的自定义(更简单易用但不太通用)方案。

可悲的是,正如我正在写这篇文章(2014年1月),Visual C ++编译器还不支持std::exception,尽管它确实支持其余大部分机器。令人高兴的是,定义这些东西并不困难。例如,谷歌搜索,我在Tomaka-17's blog找到了工作代码,这是我在下面修改的代码 - std::nested_exception 支持Visual C ++

std::nested_exception

使用g ++ 4.7.2,定义这个东西会更加复杂,但是由于g ++ 4.8.2已经有了它,所以没有必要:对于g ++,只需在必要时升级编译器。

然后下一个问题是如何检索嵌套异常信息

基本上归结为迭代重新抛出并捕获每个嵌套异常,例如:如下:

#include <exception>        // std::rethrow_exception

// For Visual C++ define CPPX_NORETURN as "__declspec(noreturn)"
#ifndef CPPX_NORETURN
#   define  CPPX_NORETURN [[noreturn]]
#endif

// Visual C++ 12.0 lacks these things.
// Code adapted from http://blog.tomaka17.com/2013/07/c11-nested-exceptions/.

#if !defined( GOOD_COMPILER )
#include <utility>          // std::forward
#include <type_traits>      // std::remove_reference
namespace std {
    class nested_exception
    {
    private:
        exception_ptr nested;

    public:
        CPPX_NORETURN
        void rethrow_nested() const
        { rethrow_exception(nested); }

        exception_ptr nested_ptr() const { return nested; }

        virtual ~nested_exception() {}
        nested_exception() : nested( current_exception() ) {}
    };

    template< class Type >
    CPPX_NORETURN
    void throw_with_nested( Type&& t )
    {
        typedef remove_reference<Type>::type Pure_type;

        struct Unspecified_mi_type
            : nested_exception
            , Pure_type
        {
            Unspecified_mi_type( Type&& t )
                : Pure_type( forward<Type>( t ) )
            {}
        };

        if (is_base_of<nested_exception, Pure_type>::value)
        {
            throw forward<Type>( t );
        }
        else
        {
            throw Unspecified_mi_type( forward<Type>( t ) );
        }
    }

    template< class X >
    void rethrow_if_nested( X const& x )
    {
        if( auto const ptr = dynamic_cast< nested_exception const* >( &x ) )
        {
            ptr->rethrow_nested();  // It's specified to do this, C++11 §18.8/8.
        }
    }
}
#endif // not GOOD_COMPILER

输出:

!Resource_manager::foo failed
!<because> Loading failed

免责声明:我为此答案编写了上述代码,因此未经过广泛测试。但无论如何,享受!