{fmt} 版本演进全解析:从 C++ Format 到现代格式化库的历史变更深度指南 发布时间:2026/9/10 19:07:36 尧图网站设计 {fmt} 版本演进全解析从 C Format 到现代格式化库的历史变更深度指南【免费下载链接】fmtA modern formatting library项目地址: https://gitcode.com/GitHub_Trending/fm/fmt本文以仓库 doc/ChangeLog-old.md 的完整历史变更记录为骨架梳理 {fmt} 从 v0.8.02014 年首发到 v7.1.32020 年期间的核心技术演进——浮点格式化算法Grisu → Dragonbox、编译期格式字符串处理FMT_COMPILE、API 现代化与std::format兼容对齐。读完本文你将理解 {fmt} 每个关键能力诞生的版本节点、对应头文件与宏开关并能基于当前仓库include/fmt 下的 core.h、format.h、compile.h、format-inl.h 等追溯这些特性的真实实现。一、文档定位与阅读方式doc/ChangeLog-old.md是一份按版本倒序排列的历史变更日志覆盖 v7.1.32020-11-24回溯至 v0.8.02014-04-14的 40 余个版本。它既记录了新功能、破坏性变更Breaking、性能优化的演进脉络也保留了大量可复现的最小代码示例与编译期错误信息是理解 {fmt} 设计哲学性能优先、与std::format兼容、可裁剪适配嵌入式的一手资料。阅读建议先按功能主线浮点格式化、编译期格式化、核心 API、颜色输出、chrono、ranges、构建选项横向归纳再按版本节点纵向追踪 API 弃用与替换关系可避免被逐条 issue 链接淹没。二、浮点格式化引擎的两次革命6.1.0 → 7.1.02.1 v6.1.0最短十进制表示成为默认在 6.1.0 之前{fmt} 浮点输出并不保证最短表示。6.1.0 起IEEE 754float/double默认采用最短十进制表示 正确舍入shortest round-trip#include cmath #include fmt/core.h int main() { fmt::print({}, M_PI); // 输出 3.141592653589793 }同版本还修复了float被提升为double的老问题0.1f直接输出0.1而非0.10000000149011612并新增 128 位整数格式化支持__int128_t。变更日志给出了当时 dtoa-benchmark 的对照表数据仅代表文档发布时该基准环境的结果函数耗时 (ns)加速比ostringstream1,346.301.00xostrstream1,195.741.13xsprintf995.081.35xdoubleconv99.1013.59xfmt88.3415.24x2.2 v6.0.0实验性 Grisu 算法6.0.0 引入了默认关闭的实验性 Grisu 浮点格式化通过FMT_USE_GRISU宏开启#define FMT_USE_GRISU 1 #include fmt/format.h auto s fmt::format({}, 4.2); // 使用 Grisu 格式化2.3 v7.1.0切换到 Dragonbox7.1.0 将默认浮点算法从 Grisu3 升级为Dragonboxjk-jeon实现保证最短十进制表示、round-trip 与正确舍入并支持通过FMT_USE_FULL_CACHE_DRAGONBOX宏以更大二进制体积换取更高性能。从当前仓库源码可以印证这一演进include/fmt/format-inl.h第 38–44 行会根据编译器设置自动选择完整缓存模式__OPTIMIZE_SIZE__下自动置 0否则默认置 1第 207 行起即为 Dragonbox 算法的实现dragonbox::to_decimal、dragonbox::umul192_upper128等include/fmt/format.h第 1626 行起也内嵌了同一算法的精简版本供头文件模式使用。换句话说当前仓库的浮点路径已经是 Dragonbox 的最终形态。2.4 浮点格式化的行为细节#说明符输出尾零7.1.0fmt::print({:#.2g}, 0.5)输出0.506.2.0 起{:#.0f}对 42.0 输出42.保证小数点存在。默认不带.07.1.0为与std::format、std::to_chars保持一致默认浮点输出不再追加.0需要小数点与尾零时用#。任意精度7.1.0fmt::print({:.500}\n, 4.9406564584124654E-324)可完整打印 IEEE 754 次正规数denormal的 500 位十进制展开——这得益于用内部 IEEE 754 格式化器替换了snprintf回退路径。locale 独立性6.0.0默认浮点输出不受全局 locale 影响如ru_RU.UTF-8下仍输出4.2需要本地化分隔符时使用n/L说明符。精度溢出检测6.2.0与罕见尾零修复7.1.0如避免输出1.00e-34持续修正浮点边界。三、编译期格式字符串处理FMT_COMPILE 与 format string compilation3.1 从 fmt::compile 到 FMT_COMPILEv6.0.0引入实验性fmt::compileint({})把格式字符串解析移出格式化函数适合反复格式化同类型对象#include fmt/compile.h auto f fmt::compileint({}); std::string s fmt::format(f, 42); // s 42v7.0.0推出更简单高效的编译期格式化 API#include fmt/compile.h // 将 42 转为 std::string无任何运行时格式串解析 std::string s fmt::format(FMT_COMPILE({}), 42);旧的fmt::compile自此弃用。3.2 极致优化单条指令写字符7.1.0 改善了FMT_COMPILE对单个码元的处理format_to(buf, FMT_COMPILE(x{}), 42)在 Godbolt 上被优化为仅数条指令其中一条movb $120, (%rdi)直接把x0x78120写入输出缓冲。这展示了编译期格式化把解析成本归零后的汇编级收益。3.3 组合能力动态宽度7.1.0格式串编译支持动态宽度参数。format_to_n 编译版本7.1.0#include fmt/compile.h int main() { char buffer[8]; fmt::format_to_n(buffer, sizeof(buffer), FMT_COMPILE({}), 42); }当前实现位于 include/fmt/compile.h 第 538 行起的format_to_n重载返回format_to_n_resultOutputIt。FMT_STRING 与编译期检查FMT_COMPILE底层依赖FMT_STRING机制见 compile.h 第 38–40 行FMT_STRING_IMPL(s, fmt::compiled_string)配合FMT_ENFORCE_COMPILE_STRING定义于 base.h 第 2392 行可强制要求所有格式串必须使用编译期形式。3.4 相关性能优化7.0.0整数格式化format_to 编译期格式串 栈上缓冲在 libc/libstdc 上都快于to_chars。小格式串优化fmt::format(Result: {}: ({},{},{},{}), ...)提速约 40%。外部模板extern templates显著降低编译时间macOS clang、-O2下单个测试翻译单元从 2.31s 降至 0.30s-O0从 0.60s 降至 0.30s。命名参数改为栈上存储零动态内存分配7.0.0 展示的汇编中fmt::arg(answer, 42)完全内联进栈帧。四、核心 API 的现代化与精简5.x → 7.x4.1 轻量核心头文件 fmt/core.hv5.0.0 引入fmt/core.h通过裁剪标准头依赖大幅降低编译开销与代码膨胀#include fmt/core.h fmt::print(The answer is {}., 42);v7.1.0 进一步把format_to_n与formatted_size纳入核心 API#include fmt/core.h int main() { char buffer[10]; auto result fmt::format_to_n(buffer, sizeof(buffer), {}, 42); }4.2 参数捕获与错误报告make_format_args / make_args_checkedv5.0.0 提供make_format_args捕获参数v7.1.0 新增make_args_checkedArgs...(format, args...)既做编译期格式串检查又能避免二进制膨胀典型用法是封装带FMT_STRING的日志宏void vlog(const char* file, int line, fmt::string_view format, fmt::format_args args) { fmt::print({}: {}: , file, line); fmt::vprint(format, args); } template typename S, typename... Args void log(const char* file, int line, const S format, Args... args) { vlog(file, line, format, fmt::make_args_checkedArgs...(format, args...)); } #define MY_LOG(format, ...) \ log(__FILE__, __LINE__, FMT_STRING(format), __VA_ARGS__) MY_LOG(invalid squishiness: {}, 42);不可格式化类型错误信息改进v6.2.0 起fmt::format({}, S())直接给出static_assert失败v7.1.0 进一步把类型名嵌入断言消息formattablehow_about_no()当前实现可追溯到 base.h 第 1682 行起的formattable常量化检查与第 2271 行的type_is_unformattable_for。4.3 动态参数存储v6.2.0 引入实验性dynamic_format_arg_store可在运行时构建参数列表fmt::dynamic_format_arg_storefmt::format_context store; store.push_back(answer); store.push_back(42); fmt::vprint(The {} is {}.\n, store); // The answer is 42.v7.0.0 为其补齐命名参数、clear与reserve支持。4.4 迭代器与输出约束format_to 迭代器版5.0.0fmt::format_to(std::back_inserter(out), {}, 42)。format_to_n5.0.0限制输出字符数char out[4]; fmt::format_to_n(out, sizeof(out), {}, 12345)得到1234不含\07.1.0 修复了缓冲边界处理issues 1996/2029。formatted_size5.0.0auto size fmt::formatted_size({}, 12345); // 5。format_to text_style7.1.0fmt::format_to(std::back_inserter(out), fmt::emphasis::bold | fg(fmt::color::red), The answer is {}., 42)。4.5 命名空间与命名规范v5.0.0 按标准库惯例统一小写命名向 C20 标准化提案 P0645R2 提交子集。v6.1.0 起basic_format_parse_context取代basic_parse_context。v7.0.0 将internal命名空间更名为detail保留FMT_USE_INTERNAL别名开关并引入 inline 命名空间做符号版本化FMT_USE_INLINE_NAMESPACES宏可控制其启用见 base.h 相关定义。v5.0.0 起变参函数以v前缀区分vformat/vprint接收format_args变参模板format/print作为便捷入口。五、与 std::format 的兼容对齐7.0.0 为分水岭7.0.0 集中完成了一批面向 C20std::format的兼容改造迁移期均提供宏开关变更说明兼容宏c说明符整型支持字符格式5.x 提出、7.0 落地—n→Llocale 数字格式说明符更名6.2.0 起L7.0.0 弃用nFMT_DEPRECATED_N_SPECIFIER数值对齐默认关闭FMT_DEPRECATED_NUMERIC_ALIGN移除FMT_DEPRECATED_PERCENT%说明符与std::format冲突—移除FMT_STRING_ALIAS/fmt宏统一使用FMT_STRING—移除convert_to_int、*parse_context、*writer等由编译期格式化 API 替代—当前仓库 include/fmt/format.h 中FMT_REDUCE_INT_INSTANTIATIONS第 170–175 行默认 0即为 7.1.0 引入的二进制瘦身选项牺牲少量整数格式化性能换取代码体积面向内存极度受限的嵌入式场景可通过 CMake 选项开启。六、chrono、ranges、color三类扩展头文件的演进6.1 chrono 时间日期格式化fmt/chrono.hv5.3.0实验性 chrono 格式化{:%H:%M:%S}支持 strftime 风格说明符fmt::print(Default format: {} {}\n, 42s, 100ms); // 42s 100ms fmt::print(strftime-like format: {:%H:%M:%S}\n, 3h 15min 30s); // 03:15:30v6.0.0实现%Q数值与%q单位、浮点 duration 精度{:.1}输出1.2s、chrono 动态宽度移除旧fmt/time.h改用fmt/chrono.h。v7.1.0新增std::chrono::time_pointsystem_clock格式化#include fmt/chrono.h int main() { auto now std::chrono::system_clock::now(); fmt::print(The time is {:%H:%M:%S}.\n, now); }当前实现位于 include/fmt/chrono.h并配套测试 test/chrono-test.cc。6.2 范围与容器格式化fmt/ranges.hv5.0.0实验性 ranges 支持fmt::print({}, std::vectorint{1,2,3})输出{1, 2, 3}。v6.1.0新增 tuple 格式化(a, 1, 2.0)fmt::join接受initializer_list6.2.0。v7.0.0fmt::join支持 sentinel哨兵范围struct zstring_sentinel {}; bool operator(const char* p, zstring_sentinel) { return *p \0; } bool operator!(const char* p, zstring_sentinel) { return *p ! \0; } struct zstring { const char* p; const char* begin() const { return p; } zstring_sentinel end() const { return {}; } }; auto s fmt::format({}, fmt::join(zstring{hello}, _)); // h_e_l_l_ov7.1.0支持非 constbegin/end范围如ranges::views::filter视图、恢复 ostream 范围格式化、memory_buffer::append接受范围。早期源头v4.0.0 的fmt::join(v.begin(), v.end(), , )可配合{:06.2f}等格式符。当前实现见 include/fmt/ranges.h测试见 test/ranges-test.cc。6.3 颜色与终端输出fmt/color.hv5.1.0实验性 RGB 颜色输出FMT_EXTENDED_COLORS宏启用。v5.2.0颜色支持移入fmt/color.h并默认启用旧print_colored弃用可用FMT_DEPRECATED_COLORS找回。v5.3.0支持强调样式bold/italic/underline/strikethrough与 4-bit 终端色fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, Hello, {}!\n, world); print(fg(fmt::terminal_color::red), stop\n);v6.0.0format/vformat支持text_stylev6.1.0带text_style的print变为原子操作多线程输出不交错。当前实现见 include/fmt/color.h测试见 test/color-test.cc。七、printf 兼容层fmt/printf.hv0.10.0实验性 printf 格式支持fmt::printf(%d, 42)、fmt::sprintf。v3.0.0长度修饰符ll等变为可选类型自动推断%lld传-42正确输出std::printf中是 UB符号在错误说明符下保留。v4.0.0printf.h独立成头%s成为通用说明符%.f更贴近标准 printf支持自定义参数格式化。v5.3.0新增make_printf_args/make_wprintf_args参数捕获函数。v7.0.0fmt::printf与标准规格的兼容性持续改进issues 1595/1682/1683/1687/1699。当前实现见 include/fmt/printf.h测试见 test/printf-test.cc。八、ostream 与用户自定义类型fmt/ostream.hv3.0.0operator支持移入fmt/ostream.h可格式化任意提供operator的类型如Date类。v6.0.0formatter特化优先级高于operator特化输出 2流输出 1移除fmt::visit等旧接口5.3.0 弃用。v6.1.0locale 传入 ostream 插入运算符fmt::format(std::locale(fr_FR.UTF-8), {}, S{0.42})输出0,42。v7.1.0fmt::to_string在无formatter特化时回退到 ostreamoperator修复既有隐式转换又有重载operator的类型处理issue 1766。当前实现见 include/fmt/ostream.h测试见 test/ostream-test.cc。九、构建、宏开关与嵌入式适配9.1 CMake 选项演进选项/宏引入版本作用FMT_DOC/FMT_INSTALL/FMT_TEST2.0.0控制 doc/install/test 目标生成子项目默认关闭见根 CMakeLists.txt 第 87–89 行FMT_OS7.0.0控制 OS 相关 API 是否编译进 fmt 目标CMakeLists.txt 第 92、301–304 行FMT_USE_FLOAT/FMT_USE_DOUBLE/FMT_USE_LONG_DOUBLE6.2.0禁用浮点格式化内存受限嵌入式FMT_REDUCE_INT_INSTANTIATIONS7.1.0减少整数格式化模板实例化以缩小二进制FMT_HEADER_ONLY1.0.0头文件模式定义后无需链接库FMT_USE_INLINE_NAMESPACES7.1.0控制 inline 命名空间符号版本化FMT_FUZZ7.0.0替代FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION避免干扰使用方模糊测试FMT_ARM_ABI_COMPATIBILITY7.1.1规避 ARM 上 GCC/Clang ABI 不兼容9.2 弃用与移除路线迁移要点v3.0.0项目由cppformat 更名为 fmt头文件统一#include fmt/format.h。v5.0.0要求 C11变参模板 类型萃取移除FMT_VARIADIC_*模拟宏C98 用户停留在 4.x。v6.2.0OS 相关 APIwindows_error等从fmt/format.h迁至fmt/os.hFMT_DEPRECATED_INCLUDE_OS过渡。v7.0.0集中移除FMT_STRING_ALIAS、convert_to_int、*writer、FMT_DEPRECATED_PERCENT等详见上文第五节表格。9.3 安装与分发v2.0.0Debian/Ubuntu 提供sudo apt-get install libcppformat1-devHomebrew 提供brew install cppformat当时名称。v5.3.0新增 pkgconfig 支持方便 meson 等构建系统消费当前模板见 support/cmake/fmt.pc.in。v6.1.0可通过 conda-forge 安装conda install -c conda-forge fmt。v5.0.0头文件迁至include/fmt/*.h源码迁至src目录与当前仓库布局一致并提供 Android NDK 构建支持support/Android.mk。十、质量保障模糊测试、平台覆盖与警告治理模糊测试v6.0.0 接入 oss-fuzzissue 1199v6.1.0 起 CUDA 测试改为FMT_CUDA_TESTCMake 选项显式开启v7.0.0 以FMT_FUZZ宏替代全局模糊宏。仓库 test/fuzzing 目录保留 chrono、浮点、命名参数等模糊测试用例。编译器矩阵各版本围绕 MSVC含 2013/2015、GCC含 4.4/4.8/7/8/9、Clang含 Apple LLVM、MinGW、nvcc、Intel C 修复大量警告与链接问题如 7.1.1 修复 GCC LTO 链接错误、7.1.0 移除windows.h依赖。平台兼容持续覆盖 Android NDK、FreeBSD、OpenBSD、Cygwin、Hurd、IBM i无uintptr_t的 AS/400、emscripten 及-fno-exceptions/-fvisibilityhidden等特殊编译配置。测试佐证当前仓库 test 目录下的 format-test.cc、chrono-test.cc、ranges-test.cc、color-test.cc、printf-test.cc、os-test.cc 等即对应上述各功能模块的回归验证。十一、写给迁移者的时间线速查C11 门槛若仍在用 C98停留在 4.x5.0.0 需要 C11。头文件路径3.0.0 起一律#include fmt/xxx.h核心用法优先fmt/core.h减少编译时间。编译期格式化7.0.0 使用FMT_COMPILE(...)旧fmt::compile弃用。浮点输出7.1.0 为 Dragonbox 最短表示期望小数点/尾零显式用#locale 分隔符用L替代n。扩展头文件颜色用 fmt/color.h、时间用 fmt/chrono.h、范围用 fmt/ranges.h、printf 用 fmt/printf.h、ostream 用 fmt/ostream.h、OS 能力用 fmt/os.h。嵌入式裁剪组合FMT_OSOFF、FMT_USE_FLOAT0、FMT_REDUCE_INT_INSTANTIATIONS1与FMT_HEADER_ONLY可最小化体积与依赖。发布节奏7.1.x 系列以 ABI 兼容与缺陷修复为主7.1.1 修复 7.0.x ABI 兼容、7.1.3 修复共享库链接与format_to_n缓冲边界升级同主版本内小版本风险最低。延伸阅读本文所述功能在当前仓库均有对应实现与测试可交叉验证建议结合 doc/api.md完整 API 参考与 doc/get-started.md上手教程进一步实践doc/ChangeLog-old.md为历史变更档案最新变更请查阅根目录 ChangeLog.md。【免费下载链接】fmtA modern formatting library项目地址: https://gitcode.com/GitHub_Trending/fm/fmt创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考 网站设计 设计经验 学习干货 返回列表