如何用 folly result<T> 替代异常返回并用 or_unwind 传播错误

发布时间:2026/9/13 18:17:58

如何用 folly result<T> 替代异常返回并用 or_unwind 传播错误 如何用 folly result 替代异常返回并用 or_unwind 传播错误【免费下载链接】follyAn open-source C library developed and used at Facebook.项目地址: https://gitcode.com/GitHub_Trending/fol/folly在 C 服务代码里如果你希望某些关键路径不再抛异常、错误必须被显式检查和处理folly 的folly/result提供了resultT函数不再throw而是返回resultT其中要么装着值要么装着错误std::exception_ptr或 stopped取消状态。配合协程语法co_await or_unwind(...)未处理的错误会沿调用链向上传播类似 Rust 的?运算符。本文的任务是把一段抛异常的同步函数改写成resultT协程用or_unwind传播错误并在调用点用get_exception/get_rich_error_code完成处理与验证。前提C20 与协程支持result及相关宏由FOLLY_HAS_RESULT门控。在 Portability.h 中可以看到判定条件必须同时满足 C20FOLLY_CPLUSPLUS 202002L且编译启用了协程支持FOLLY_HAS_COROUTINES否则FOLLY_HAS_RESULT为 0result的协程能力包括or_unwind不可用。因此你的构建需要 C20 标准和支持协程的编译器。用到本场景需要包含的头文件#include folly/result/result.h // resultT, error_or_stopped, get_exception #include folly/result/coro.h // or_unwind, or_unwind_owning // 需要错误上下文时再加 #include folly/result/or_unwind_epitaph.hAPI 细节以头文件内的 docblock 为准完整用法见 result.md。第一步函数返回 result 错误不再 throwresultT返回的函数体写成协程即可。错误分支不throw而是返回error_or_stopped{异常对象}// 这个 result coro 就像一个返回 result 的普通同步函数 // 但它等价于函数体被 try {} catch(...) { // return error_or_stopped::from_current_exception(); } 包围 resultsize_t countGrapefruitSeeds() { // 若 getFruitBox() 返回 error 或 stopped // co_await or_unwind 会立即把该状态向上传播 auto box co_await or_unwind(getFruitBox()); auto boxRes box.findFruit(FruitTypes::GRAPEFRUIT); if (auto ex get_exceptionRottenFruit(boxRes)) { logDiscardedGrapefruit(ex); // ex 用起来像 const RottenFruit* return 0; } const auto grapefruit co_await or_unwind(boxRes); size_t numSeeds 0; // fetchSegments() 返回的不是 result它抛出的异常 // 会被捕获并打进本函数的返回值 auto segments grapefruit.fetchSegments(); for (auto segment : segments) { numSeeds (co_await or_unwind(segment.seeds())).size(); } co_return numSeeds; }这段代码来自 result.md 的 Use-case 1 示例其中getFruitBox、findFruit、RottenFruit等为示意符号替换为你自己的函数与异常类型即可。要点result协程而不是普通函数是异常边界函数体内任何未被捕获的异常都会被自动捕获进返回值调用方可以假定该函数不会向外抛异常除std::bad_alloc及参数拷贝/移动构造之外。resultT是[[nodiscard]]单写一句resultFoo()不处理会编译失败你必须显式co_await or_unwind(resultFoo())从机制上杜绝忘记检查返回值。不要给result协程加noexcept——文档明确说明这会导致参数构造抛异常或std::bad_alloc时std::terminate。第二步用 co_await or_unwind 向上传播or_unwind定义见 coro.h支持的类型有resultT、value_only_resultT和error_or_stopped。行为规则参数是值取出值返回是 error 或 stopped立即短路把该状态传给当前协程的 awaiter向上传播。取值方式影响拷贝co_await or_unwind(resFn())对右值返回T错误路径上exception_ptr以 move 传播约 1ns对左值co_await or_unwind(res)返回T错误路径上exception_ptr是拷贝约 7ns。错误路径热时用std::move(res)冷路径优先可读性。不要用auto ref co_await or_unwind(resFn())这是文档标注的已知悬垂问题见 result.md 的 Known issue 一节引用绑定到临时结果后在语句结束即失效。安全写法是auto val co_await or_unwind(resFn());按值存储、co_await or_unwind_owning(resFn())或先存结果再std::move传递。处理特定错误时用folly::get_exception替代try-catch多类错误用else if链式判断result handlesErrors() { auto r propagatesErrors(); if (auto* ex get_exceptionMyErr(r)) { // 处理 ex用起来像 const MyErr* } else { auto v co_await or_unwind(std::move(r)); // 未处理的 error 或 stopped 继续传播 } }配合rich_error体系见 rich_error.mdget_exceptionErr(res)返回一个指针-like 对象直接支持fmt和operator会打印错误码、消息、源码位置和传播上下文epitaph 栈。注意查询时写具体的Err类型不要写rich_errorErrif (auto err get_exceptionFruitError(res)) { // 注意不是 FruitError* // 打印 code、message、source location 与传播上下文 LOG(ERROR) Failed to peel fruit: err; }如果只需要错误码用get_rich_error_codeCode(container)返回std::optionalCode查不到 code不等于没有错误错误可能用了别的 code 或不支持 code 协议。可选用 epitaph 给错误附加传播上下文错误沿调用链传播时会丢失在哪一层出的问题。epitaph包装器可以给 error/stopped 状态附加一条消息和源码位置构建轻量错误栈or_unwind_epitaph是or_unwind(epitaph(...))的语法糖见 or_unwind_epitaph.h值路径上跳过 epitaph 零开销错误路径才附加// 来自 result.md co_return co_await or_unwind_epitaph( resultFn(), context {}, arg); // 等价的手写形式来自 rich_error.md co_await or_unwind(epitaph(resultFn(), in {} due to {}, place, reason));epitaph 是透明的get_exceptionEx()访问到的是底层错误而非包装器。热路径可以不加 epitaph——文档给出的当前开销约为 60ns属于可选项按需启用。验证跑仓库内的 demo 或按同样的模式检查仓库提供了一个可直接阅读的完整示例 demo/basketball.cpp它演示了错误产生make_coded_rich_error、带 epitaph 的传播or_unwind(epitaph(...))以及调用点的完整处理循环int main() { Player inbounder, pointGuard, shootingGuard; pointGuard.passingLaneBlocked true; while (true) { auto points runFastBreak(inbounder, pointGuard, shootingGuard); if (points.has_value()) { return points.value_or_throw(); } if (auto ex get_exceptionstd::exception(points)) { fmt::print(Debug log: {}\n, ex); } if (std::errc::resource_unavailable_try_again get_rich_error_codestd::errc(points)) { pointGuard.passingLaneBlocked false; fmt::print(retrying after clearing the lane\n); continue; } return 0; // Score no points (currently unreachable) } }main()里的has_value()/get_exception/get_rich_error_code三连就是验证错误处理的检查方式先区分值与错误再按类型查具体异常最后按 code 做分支demo 中std::errc只是示意文档特意注明真实代码不要拿std::errc当业务错误码。README.md 给出了该 demo 在错误路径上的文档示例输出不是每次运行都必须一致的固定日志源码位置和行号会随版本变化passing lane blocked - std::errc11 (Resource temporarily unavailable) result/demo/basketball.cpp:39 [via] fast break collapsed result/demo/basketball.cpp:47其中[via]后面就是 epitaph 添加的上下文。你自己的代码若也能打出错误消息 来源位置 [via] 传播上下文说明 rich error 与 epitaph 链路工作正常。限制与非协程替代路径以下约束来自文档直接影响你能怎么写调试构建下的硬性校验error_or_stopped若装入空std::exception_ptr或OperationCancelled在 debug 构建中会std::terminate见 result.h 顶部 docblock。取消应使用stopped_result/has_stopped()而不是抛OperationCancelled。value_or_throw()只用于边缘场景在result协程里它就是co_await or_unwind(res)的低效写法文档明确不推荐若写非协程的热路径函数先显式if (res.has_value())再用它让throw 分支不可能发生。非协程替代路径老编译器等确实不能用协程时可以写普通函数返回resultT调用方无感知差异但必须遵守基本不抛契约。result.md 给出的写法是用result_catch_all包住函数体内部错误用return rh.error_or_stopped();手动传播#include folly/result/result.h resultint plantSeeds(int n) { return result_catch_all([]() - resultint { if (n 0) { return error_or_stopped{std::logic_error{cannot plant 0 seeds}}; } int seedsLeft n; for (int i 0; i n; i) { auto rh digHole(i); if (auto ex get_exceptionHitBigRock(rh)) { continue; // 跳过这颗种子 } else if (!rh.has_value()) { return rh.error_or_stopped(); // 未处理的 error 或 stopped } rh.value_or_throw().plantSeed(i); --seedsLeft; } return seedsLeft; }); }性能预期文档基准供决策参考错误传播是 O(1ns) 级而throw超过 1µsor_unwind错误路径为 3-30ns值路径每个函数调用多约 1 个分支的开销。富错误整体比整数错误码慢约 10 倍、比抛异常快 10-200 倍高频错误条件可考虑immortal_rich_errorconstexpr 单例构造/销毁 5ns。需要与异步folly::coro任务互操作时同一套语法仍然成立res co_await value_or_error_or_stopped(task())拿到result后照常co_await or_unwind(res)纯向上传播异常则继续用co_await co_nothrow(childTask())。与旧Try代码的互操作通过result_to_try()/try_to_result()完成选型依据见 design_notes.md。【免费下载链接】follyAn open-source C library developed and used at Facebook.项目地址: https://gitcode.com/GitHub_Trending/fol/folly创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/13 18:12:58

基于PyTorch的农作物病虫害识别:迁移学习与图像分类实战

简介:农作物病虫害识别系统是一份基于机器学习(Python)的完整毕业设计项目,适合计算机、人工智能等相关专业学生用于课程设计、论文实验或期末项目,也适合作为图像分类入门的工程范例。资源共477个文件,压缩…

2026/9/13 18:12:58

基于RPA的企微私域管理系统架构与实战复盘

2026年是农历丙午马年,对不少做私域运营的团队来说,新年的头等大事不是换slogan,而是把手里越来越重的人工活真正“盘活”。企业微信的客户联系能力越来越强,可日常运营里的手工环节也跟着多了:加好友要导入&#xff0…

2026/9/13 19:13:01

企业财务管理核心模块与信息化实践指南

1. 企业财务管理业务概述企业财务管理是企业运营的核心支柱,它涵盖了资金筹集、投资决策、运营资金管理和利润分配等关键环节。作为企业管理者必备的核心能力,财务管理水平直接决定了企业的生存发展和市场竞争力。现代企业财务管理已从传统的记账核算&am…

2026/9/13 19:08:01

SSM框架开发疫情防控管理系统实战指南

1. SSM疫情防控管理系统概述SSM疫情防控管理系统是基于SpringSpringMVCMyBatis框架开发的一套综合性疫情管理平台。这个系统在2020年疫情爆发后开始被广泛应用,目前已经成为社区、学校和企业进行常态化疫情防控的重要工具。作为一个完整的Java Web项目,它…

2026/9/13 0:01:16

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/13 0:01:16

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/12 6:29:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/12 14:32:17

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/13 11:18:28

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码