
深入解读 folly::result 演进路线图folly/result/docs/future_ideas.md 中的未来扩展蓝图【免费下载链接】follyAn open-source C library developed and used at Facebook.项目地址: https://gitcode.com/GitHub_Trending/fol/follyfolly::result是 Folly 中面向 C20 的错误/值容器与短路协程模块而 folly/result/docs/future_ideas.md 正是这一模块的未来扩展路线图它系统收集了result与 rich errors富错误体系可能的发展方向并按近期→远期排列。本文以该文档为骨架结合仓库内 result、epitaph.h、rich_exception_ptr.h 等源码与其配套的docs/系列文档逐条解析这份路线图中的每一项设想——从 constexpr 优先的开发原则、协程异步栈 epitaph、result_generator到 small-value 优化、无 eptr 存储、无 RTTI/无异常支持等——帮助读者理解folly::result当前的实现边界、后续演进方向及其底层动机。一、文档定位一份未来想法清单与三条开发准则future_ideas.md明确表示它收集的是folly::result与 rich errors 体系中一部分可能的未来扩展其中最重要的想法各自拥有独立的docs/future_....md文档如 future_epitaph_in_place.md、future_fast_rtti.md、future_small_value.md。整份清单大致按近未来到远未来排序。在展开各项想法之前文档先给出了构建新功能时应遵循的三条原则这些原则直接反映在仓库现有代码的组织方式中尽可能做成constexpr并用constexpr测试。原因有三C26 将使std::exception可在常量求值代码中使用用common.h中的test()编写 constexpr 测试能获得极强的 UB 检测能力——consteval 解释器甚至拒绝编译依赖未定义行为的代码由于大量错误是静态的把分配与构造搬到构建期有利于性能。仓库中 test/rich_exception_ptr_constexpr.cpp 正是对constexprrich_exception_ptrimmortal 形式的落地验证。rich errors 拥有巨大的测试矩阵(packed/separate storage) × (owned/immortal/misc errors) × (with-epitaphs/underlying errors) × (move/copy/assign/compare/format fundamentals)。当前 rich error 单元测试优先测试深度——通过精心构建辅助函数来覆盖大测试矩阵避免一堆临时拼凑的 ad-hoc 测试场景同时也要提供usage example风格的集成测试。从 test/ 目录下rich_exception_ptr_owned_test.cpp、rich_exception_ptr_immortal_test.cpp、rich_exception_ptr_misc_test.cpp、rich_exception_ptr_fundamentals_test.cpp等按存储类别拆分测试文件的命名方式可以直观看到这一测试矩阵的组织思路。涉及位判别 variantbit-discriminated variant代码时必须获得专家评审——因为这类代码如rich_exception_ptr的低 3 位对齐位判别在本质上就游走在可接受的 UB边缘。二、近中期路线图从格式化到协程栈跟踪2.1resultT是否应随T支持 fmt /(ostream)格式化文档对当T可格式化时resultT是否也应可格式化持谨慎态度虽然方便但难以界定值输出与非值输出的区分约定强行加糖可能反而有害。目前result本身不做这种隐式糖化但错误的输出侧已经有完整方案——rich_ptr_to_underlying_errorEx返回的指针状对象同时支持fmt与operator并且能打印完整 epitaph 栈详见 epitaphs.md 与 demo/basketball.cpp 中passing lane blocked - std::errc11 ... [via] fast break collapsed的输出示例。2.2 扩展stack_epitaph以捕获协程异步栈stack_epitaph目前捕获的是原生调用栈。文档设想借助getAsyncStackTraceSafe()见folly/coro的AsyncStack代码即 folly/coro/AsyncStack.h进一步捕获协程异步栈从而展示完整的协程调用链而不只是unhandled_exception点的原生栈。仓库当前实现中epitaph.h 的stack_epitaph_opts已支持内联帧存储策略默认max_frames 256、inline_frames 17当内联容量不足时多余帧会溢出到引用计数的堆分配帧存储是零终止的uintptr_t数组frames_[kInline 1]并以has_stack区分普通 source-location 与栈捕获两种位置策略。而 detail/result_promise.h 中的unhandled_exception()已调用stack_epitaph_for_unhandled_exception(value_-eos_)实现在 epitaph.cpp说明未处理异常自动带栈这一能力已经存在未来路线是把它从原生栈升级到协程异步栈。相关测试可参考 test/stack_epitaph_test.cpp。2.3in_place/in_place_t支持与TEST(Result, throwingMove)清理文档建议在公共 API 中审慎加入in_place/in_place_t构造支持当result获得该能力后可清理 test/result_test.cpp 中TEST(Result, throwingMove)里对.value的重赋值。这是一个 API 面完善方向对应result当前可用但可再打磨的构造语义。2.4 将部分 epitaph 标记为 debug-only并支持运行时切换设想让某些 epitaph 仅存在于 debug 构建且理想情况下能像VLOG那样在运行时按需开启用于排查生产问题。这与 epitaphs.md 强调的热路径可退出、可调试性保留在其它路径的设计哲学一脉相承——epitaph目前给错误加上下文的开销约 60nsctor dtor未来 V0 版本目标是摊销到几纳秒这与下一节 in-place epitaphs 优化直接相关。2.5 审视error_or_stopped的 moved-out 行为文档提出一个待定问题error_or_stopped被移出后的行为目前是dfatal 崩溃 / 空 eptr是否令人满意尚未有定论。这一讨论涉及 result.md 中存储空std::exception_ptr属于契约违规debug 构建下致命opt 构建下直到后续断言如exception_wrapper::throw_exception中的std::terminate才暴露的既有约定。2.6 新建result/containers.hresultT map_at(Map, Key)设想以result为返回类型模仿std中会抛异常的容器访问模式如map.at()提供map_at(Map, Key)等可失败访问器。这与 result.md 中Reference support allows fallible accessors如resultValue at(const Key)的既有能力相互印证——resultT引用存储正是为这类找不到就返回错误的访问器准备的。2.7get_rich_error_code返回可格式化、带 provenance 的rich_codeCode当前folly::result已通过errc_rich_error.h、coded_rich_error.h、nestable_coded_rich_error.h支持类型擦除的错误码并用get_rich_error_code()以 RTTI-free 方式在 5ns 内取回见 design_notes.md。路线图设想增加一个返回rich_codeCode的重载或新 verb使错误码本身也能携带 epitaphprovenance并参与格式化。2.8result_generator不抛异常的生成器设想一个类似std::generator、但 yield 的是result的生成器让生成器错误不必通过抛出传播任何错误都会结束数据流并支持or_unwind语义。文档提到其草稿实现基于folly::coro::Generator见 folly/coro/Generator.h尚需更新。这也呼应了 design_notes.md 中fallible implicit conversions, enabling range-for loops over result generators的既有设计目标。2.9 rich error / result 格式化器选项化格式器未来可能需要解析一些选项来自定义输出风格分隔符 / 缩进以及省略 epitaph例如checkEptrRoundtrip测试需要。文档特别提醒在加选项前务必先让默认输出风格被广泛认可——因为一旦自动化开始解析该输出再改动就会变得困难。当前[via]/[after]分隔符的输出格式见 epitaphs.md就是这一默认风格的雏形。2.10nest_error系列 verb非侵入式嵌套错误文档指出现有 epitaph.h 与 nestable_coded_rich_error.h 都不是std::nested_exception的直接对应物。对于侵入式嵌套用户可通过实现next_error_for_epitaph()自行支持。但一个现成的 verb 也不难加例如nest_error(underlying_rep, next_rep) nest_error_inheriting_codes(underlying_rep, next_rep) // 是否有用待定与侵入式方案相反这种方式会把两个错误包进第三个仅用于管道的错误中。这个detail::nesting_error应对底层错误保持透明、委托访问但至少要自动捕获嵌套发生处的 source location或许加一条消息实现上可沿袭或扩展detail::epitaph_non_value。这与 epitaphs.md 中用next_error_for_epitaph()模拟std::nested_exception嵌套时会出现多个[via]分隔符的描述互为表里。三、远期路线图性能、无异常支持与声明式错误处理3.1 内部迁移到std::expected需 C23result内部应迁移到std::expected以摆脱由异常导致的空状态处理。这对应 result.md 中的现状说明目前result几乎从不为空类似folly::Expected当 C23 广泛可用后它将真正永不空。迁移后可以借std::expected的保证进一步简化result的状态机。3.2 实现 in-place epitaphs 优化future_epitaph_in_place.md这是路线图中已拥有完整设计文档的重头戏。核心动机是当前epitaph每次加一条注解都要给std::exception_ptr分配/释放内存约 60ns且 eptr 分配的 120–160 字节__cxa_exception存储对 epitaph 而言是浪费的。该设计的协议契约是一行核心接口bool rich_error_base::maybe_add_epitaph_in_place(rich_msg) noexcept;返回true当且仅当 rich error 可变、且成功把rich_msg移入其内部存储数组返回false时rich_msg保持不变。epitaph会依次尝试先做一次快速、无 RTTI 的rich_error_base查询因为分配可能比dynamic_cast更便宜再尝试maybe_add_epitaph_in_place()两者都失败才回退到动态 epitaph。存储布局上rich error 预留一个固定大小的rich_msg数组大多数 epitaph 只需要 source location 与 message16 字节一格static constexpr uint8_t kCapacity 4; // 可调 std::atomicuint8_t next_slot_{0}; std::arraynullable_rich_msg, kCapacity entries_{}; // 默认 nullnext_slot_同时充当槽位计数器与锁标志 kCapacity时可原地加 epitaph kCapacity表示已满或被锁回退到动态分配。关键操作void lock() noexcept { next_slot_.fetch_add(kCapacity, std::memory_order_release); } uint8_t count() const noexcept { return std::min(next_slot_.load(std::memory_order_acquire), kCapacity); } bool maybe_add_epitaph_in_place(rich_msg msg) noexcept { if (next_slot_.load(std::memory_order_relaxed) kCapacity) return false; auto slot next_slot_.fetch_add(1, std::memory_order_relaxed); if (slot kCapacity) return false; entries_[slot].set(std::move(msg)); return true; }关键不变式——每槽单写者fetch_add的原子性保证每个调用者拿到唯一槽位任何时刻最多一个线程写某个entries_[slot]。线程安全方面无锁、只使用 relaxed/release/acquire 原子操作正确用法下唯一所有者加 epitaph任何拷贝先lock()使所有别名禁用 in-place跟踪正确、无数据竞争错误用法下跨线程别名而不拷贝最多出现跟踪错乱但无 UB、无崩溃、无撕裂。由于rich_msg含一个指针字段exception_shared_string与 8 字节source_location16 字节原子写在多数架构上昂贵设计用nullable_rich_msg以指针作为空指示器配合 release-acquire 语义实现无撕裂读取writer 先普通写loc_再 release-store 指针reader 一旦 acquire-load 到非空指针就能保证看到完整的loc_。值得注意的是immortal 错误的三种实例constexpr、immutable/mutable singleton都不会从maybe_add_epitaph_in_place返回true——所以给 immortal 加 epitaph 的那一刻就会触发分配。完整协议、线程安全设计与代码片段见 future_epitaph_in_place.md。3.3rich_exception_ptr::operator bool特化文档认为专门化的operator bool可能比与默认构造对象比较更快但目前该惯用法只出现在nestable_coded_rich_error.h与测试中没有热代码使用。这属于有微小性能收益但优先级不高的微优化项。3.4 用 immortals 替代Indestructible设想在 result.cpp 中用 immortal 错误替代Indestructible使这些静态错误更健壮静态分配 vs 堆分配。immortalrich_error是constexpr异常的廉价可拷贝指针见 rich_exception_ptr.md构造-析构成本低于 5ns而动态 eptr 需要分配、原子引用计数、且不能constexpr。3.5 无std::exception_ptr的位状态错误存储eptr-free这是一个结构性的长期方向考虑增加一种位状态用于不依赖std::exception_ptr存储错误。动机清晰优点eptr 能引用抛出时的异常栈但——缺点 1eptr 在 x86/ARM 上要使用多达 120–160 字节堆内存大部分是__cxa_exception而 result 导向的程序往往并不需要这些缺点 2eptr 在无异常no-exceptions代码库中无法工作。文档还提醒一个实现细节Clang 与 GCC 为支撑 eptr 的__cxa_allocate_exception调用保留了紧急分配池emergency allocation pool以提升 OOM 韧性对result而言不用这个回退来存储错误反而是throw可靠性之外的一个好处但未来或许值得为result提供一个类似的回退池。这一方向的可行性在 rich_exception_ptr.md 的位打包设计中已见端倪——其 union 存储保留了SMALL_VALUE_eq位底层 3 位对齐位 61 位指针位可以表达多种无堆状态。3.6 支持 no-RTTI / no-exceptions让result成为Try的底层实现要让result成为Try的底层实现就必须支持无 RTTI / 无异常代码库。这对嵌入式系统既可行又有用但尚未排上优先级。文档给出了几条线索先搭建对应的 CI 环境 / 测试协议几乎必然要先实现 eptr-free 错误存储见 3.5folly::throw_exception见 folly/lang/Exception.h抽象了支持就 throw、否则 terminate类似工具很多缺工具时可以用kHasExceptions与kHasRttifolly/Portability.h做编译期门控。3.7 声明式错误处理operator标记 lambda现状下if (auto ex get_exceptionEx(res))的检查模式已经可用但有时声明式更佳。文档的设想是用operator给 lambda 打类型标签auto v res.value_or_handle_via( folly::typeYourErr [](const YourErr ex) { ... }, folly::typeOtherErr [](const OtherErr ex) { ... }, [](const error_or_stopped eos) { /* catch-all */ });也可以是无 catch-all 的co_await handle_or_unwind(res, ...)。文档坦言这属于语法糖紧迫性不高但静态获知完整查询类型清单后可以通过exception_ptr_get_object_hint或 future_fast_rtti.md 加速动态类型解析。3.8 small-value 优化future_small_value.mdresultT实质是Expectedstorage_type, error_or_stopped而错误侧rich_exception_ptrREP是位判别 variant已经为未来 small-value 存储预留了SMALL_VALUE_eq位。在 64 位平台上至少有 61 位可用平凡可拷贝、sizeof(T) 4 alignof(T) 4的值可存进高 4 字节并透明地按引用暴露对齐指针T*、alignof(T) 8的std::unique_ptrT能塞进 61 位但无法支持标准按引用访问需要特化驱动的访问 API一些 5–7 字节类型如struct A { char c[5]; }在 LE 系统上可移位 1–2 字节、BE 系统原位存储但受对齐浪费与非所有 padding 都可复用限制文档引用 LLVM issue #125863。优化后resultint、resultT*、resultT、resultunique_ptrT等可从 16 字节降到 8 字节。文档还保留了一份已弃用的实现草稿与位打包方案 detail/rich_exception_ptr_small_value.h含small_value_ref_guard、to_mangled/unmangle、rich_exception_ptr_valid_small_value概念等弃用原因是在 REP 内做 small-value 生命周期管理与result需要按引用暴露error_or_stopped()相冲突——因此首选设计是让result分支为small-value vs 非 small-value两套实现。详见 future_small_value.md。3.9 快速 RTTIfuture_fast_rtti.md查询rich_exception_ptr的特定异常类型通常很快尤其 immortal 实例但两类场景仍会付出 RTTI 开销查询非 immortal 异常且类型缺失时仍会命中 RTTI多 DSO 程序中命中类型可能回退到 RTTI——快速路径比较type_info*但同一类型跨 DSO 指针不同被迫走昂贵的dynamic_cast。传统答案是自定义类型注册系统用短小、cache-friendly 的类型标识符 高效查找代码。设计文档提出两种标识方案Likely-unique可能唯一用类型名的编译期哈希需 ABI 稳定mangled name 可用C26 P2996 之前没有好的构建期测长方式可在编译器与版本允许时用folly/lang/Pretty.h。哈希长度8/16/32/64 位待定——碰撞容易处理短些更好哈希相等时回退比较type_info*再到type_info::operator目标运行在 consteval VM 上宜选 SMHasher 高分短实现哈希如 xxHash32。Globally unique全局唯一要求每个 rich error 类型声明 UUID。风险是复制粘贴导致的碰撞即便概率低后果可能灾难性因此仅在 monorepo 中配合全局 grep 工具与 100% 覆盖的 linter 才可接受。好处是免去回退type_info::operatordebug 构建仍应校验以探测碰撞。附带收益rich_errorT是final的查询它可跳过基类检查该优化能使精确的rich_errorT查询极快对极热错误代码有意义。详见 future_fast_rtti.md。3.10resultT的相等性支持resultT目前不可比较相等因为folly::rvalue_reference_wrapper既不能隐式转换为被引用对象也不提供operator。若有强使用场景正确修法大概率是给该 wrapper 增加operator届时 test/result_test.cpp 中已有的// FIXME: Implement rvalue_reference_wrapper::operator注释掉的测试覆盖可以启用同时更新 test/value_only_result_test.cpp。四、C23 下的 immortal 位置自动捕获文档最后用一个独立小节讨论了一个锦上添花需求在向immortal_rich_errorMyErr, ...模板参数列表提供rich_msg的同时自动捕获source_location。今天能做的做法并不令人满意示例见 test/immortal_rich_error_test.cppconstexpr static auto myLoc source_location::current(); auto rep immortal_rich_errorMyErr, myLoc.ptr();难点在于rich_msg是非结构non-structural类型任何自动捕获都必须经由一个可隐式转换为rich_msg的结构型 helper 类型类似今天的vtagStrsource_location本身不是结构型所以 helper 只能存它的指针但在 C20 中无法从变量分配静态存储——除非该变量是constexpr而自动捕获的source_location::current()不可能是constexpr。因此要等 C23 的static constexpr局部变量支持。理想形态大致是templateconst source_location* Loc struct SourceTag { ... }; #define HERE() \ ([] { \ static constexpr auto loc source_location::current(); \ return SourceTagloc{}; \ }())截至文档写作时间2025 年底这类特性只在 GCC 上可用Clang 会错误地垃圾回收loc符号导致链接错误Godbolt 上的 MSVC 尚未支持该 C23 特性。技术上也可以绕开自建一个结构型类型用字符数组存文件名与函数名通过宏构造——本质是自己实现 source_location务必经指针间接保持rich_msg为 8 字节。但引入非标准类型是沉重的接口代价而这个用例看起来没那么关键——毕竟通常搜索一个 immortal 字符串就能找到源码位置。五、结语从路线图看folly::result的演进方向综合future_ideas.md及仓库现状folly::result的演进呈现出几条清晰主线性能下沉把 epitaph 从 60ns 级动态分配推向几纳秒级 in-place 存储future_epitaph_in_place.md把resultint等从 16 字节压到 8 字节future_small_value.md并探索无 eptr 的位状态错误存储。消除对 RTTI / 异常的依赖快速 RTTIfuture_fast_rtti.md与 no-RTTI/no-exceptions 支持为result成为Try的底层实现铺路。体验与互操作增强in_place支持、map_at容器函数、result_generator、nest_error系列 verb、声明式错误处理、协程异步栈跟踪等持续降低错误处理样板代码。向标准演进对齐内部迁移std::expected、区分 stopped/error 状态对齐 P1677/P2300、依赖 C23/26 的 constexpr 能力。对于想深入了解的读者建议按以下顺序阅读仓库内文档result.md用法与契约→ design_notes.md设计动机→ epitaphs.md错误溯源→ rich_exception_ptr.md位打包存储→ 三份future_*.md专项设计文档。本文所覆盖的每一项路线图设想均可在上述文档与 folly/result 的源码、测试中找到对应印证。【免费下载链接】follyAn open-source C library developed and used at Facebook.项目地址: https://gitcode.com/GitHub_Trending/fol/folly创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考