为什么在使用boost::copy_exception时会丢失类型信息

Why do I lose type information when using boost::copy_exception?

本文关键字:信息 类型 exception boost copy 为什么      更新时间:2023-10-16

当我使用boost::copy_exception将异常复制到exception_ptr时,我会丢失类型信息。看看下面的代码:

try {
    throw std::runtime_error("something");
} catch (exception& e) {
    ptr = boost::copy_exception(e);
}
if (ptr) {
    try {
        boost::rethrow_exception(ptr);
    } catch (std::exception& e) {
        cout << e.what() << endl;
        cout << boost::diagnostic_information(e) << endl;
    }
}

从中,我得到以下输出:

N5boost16exception_detail10clone_implISt9exceptionEE
Dynamic exception type: boost::exception_detail::clone_impl<std::exception>
std::exception::what: N5boost16exception_detail10clone_implISt9exceptionEE

所以基本上boost::copy_exception静态地复制了它得到的参数。

如果我用boost::enable_current_exception抛出异常,这个问题就解决了,就像这样。

try {
    throw boost::enable_current_exception(std::runtime_error("something"));
} catch (...) {
    ptr = boost::current_exception();
}
if (ptr) {
    try {
        boost::rethrow_exception(ptr);
    } catch (std::exception& e) {
        cout << e.what() << endl;
        cout << boost::diagnostic_information(e) << endl;
    }
}

这样做的问题是,有时异常是由不使用boost::enable_current_exception的库引发的。在这种情况下,除了逐个捕获每种可能的异常并在每种异常上使用boost::copy_exception之外,还有什么方法可以将异常放入exception_ptr中吗?

这是经过设计的,您的分析是正确的:使用的是静态类型,而不是动态类型。事实上,为了避免这种意外,在导致C++11的过程中,boost::copy_exception变成了std::make_exception_ptr。这样,current_exception(无论是Boost版本还是C++11版本)都是正确的,可以用来正确捕获当前异常。当谈到在代码中使用Boost.Exception-enabled异常时,我强烈建议使用BOOST_THROW_EXCEPTION,或者至少使用boost::throw_exception

当涉及到第三方代码时,除了您已经提到的解决方案,或者其他一些道德等价的解决方案(例如dynamic_cast,用于组成一个或多个异常类型层次结构的不同叶类,或者typeid滥用)之外,没有其他解决方案。

在这方面,异常处理与处理一个或多个多态类型层次结构相同,在这种情况下,您尝试的操作是一个虚拟副本,也称为克隆:要么您的代码被侵入性地设计为支持它(例如使用BOOST_THROW_EXCEPTION(e);而不是throw e;时的情况),要么您将痛苦地遍历继承树。请注意,您至少可以将这种痛苦重构为一个站点,这样您最终会得到例如

try {
    third_party_api();
} catch(...) {
    ptr = catch_exceptions_from_api();
}