如何从Boost异常中正确提取system_error的错误码?
从你描述的异常类型和代码行为来看,核心问题是实际抛出的异常是被Boost异常机制包装后的类型,而非直接的boost::system::system_error或std::runtime_error。你看到的boost::exception_detail::clone_impl<boost::exception_detail::current_exception_std_exception_wrapper<std::runtime_error>>就是最直接的证据——原始的system_error被包装成了std::runtime_error的容器,再被clone_impl封装,导致直接捕获system_error的分支根本无法命中。
为什么会出现这种包装?
这通常是因为Boost组件内部使用了boost::throw_exception函数抛出异常,它会自动将原始异常包装成boost::exception的派生类(也就是你看到的clone_impl),目的是为了支持Boost的异常诊断信息扩展能力,比如添加额外的上下文信息。
解决方案:解开异常包装,捕获原始system_error
我们可以先捕获boost::exception,然后从包装结构中提取并重新抛出原始异常,这样就能正常命中system_error的捕获分支了。下面是修改后的代码:
try { this->service_.run(); } catch (const boost::system::system_error& e) { // 处理未被包装的直接抛出的system_error i_->playerLog->info("Exiting with system error. Error code: {} -- What: {}", e.code().value(), e.what()); } catch (const boost::exception& e) { // 尝试识别并解开特定的异常包装 if (const auto* wrapped_ex = dynamic_cast<const boost::exception_detail::clone_impl< boost::exception_detail::current_exception_std_exception_wrapper<std::runtime_error>>*>(&e)) { try { // 重新抛出包装内保存的原始异常 std::rethrow_exception(wrapped_ex->get_current_exception()); } catch (const boost::system::system_error& se) { // 现在可以正常捕获到原始的system_error了 i_->playerLog->info("Caught wrapped system error. Error code: {} -- What: {}", se.code().value(), se.what()); return; // 处理完成后退出,避免执行后续的通用日志 } } // 如果不是目标包装类型,输出通用Boost异常诊断信息 i_->playerLog->info("Exiting with boost exception: {}", boost::diagnostic_information(e)); } catch (const std::exception& e) { // 兜底捕获其他未被处理的标准异常 i_->playerLog->info("Exiting with standard exception: {}", e.what()); }
代码说明:
- 保留原有的
system_error捕获分支,处理未被包装的直接抛出场景; - 在
boost::exception分支中,用dynamic_cast识别出你遇到的特定包装类型clone_impl<...>; - 调用
get_current_exception()获取包装内部保存的原始异常指针,再用std::rethrow_exception重新抛出; - 在内部嵌套的
try-catch中,就能正常捕获到原始的system_error,提取错误码和异常信息; - 最后添加兜底的
std::exception捕获,确保所有异常都能被妥善处理。
更通用的替代写法
如果你不想硬编码具体的包装类型名称,也可以借助Boost异常的错误信息提取能力,尝试获取原始异常指针:
catch (const boost::exception& e) { if (const auto* ex_ptr = boost::get_error_info<boost::exception_detail::current_exception>(e)) { try { std::rethrow_exception(*ex_ptr); } catch (const boost::system::system_error& se) { i_->playerLog->info("Unwrapped system error: {} - {}", se.code().value(), se.what()); } } // 输出通用诊断信息 i_->playerLog->info("Boost exception details: {}", boost::diagnostic_information(e)); }
这种写法更灵活,不需要依赖具体的包装类型名称,但需要确保你的Boost版本支持boost::exception_detail::current_exception这个错误信息类型。
内容的提问来源于stack exchange,提问作者Germán Diago

