news 2026/9/11 14:33:04

{fmt} 12.x 更新日志全解析:从 C11 格式化 API 到编译期格式化的演进路线

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
{fmt} 12.x 更新日志全解析:从 C11 格式化 API 到编译期格式化的演进路线

{fmt} 12.x 更新日志全解析:从 C11 格式化 API 到编译期格式化的演进路线

【免费下载链接】fmtA modern formatting library项目地址: https://gitcode.com/GitHub_Trending/fm/fmt

本指南以仓库根目录的 ChangeLog.md 为骨架,完整梳理 {fmt}(A modern formatting library)12.x 及历史版本的核心变更,并结合 include/fmt 头文件、src/fmt-c.cc 实现与 CMakeLists.txt 构建配置,逐条解读新 API 的用法、性能优化的依据与迁移要点。读完本文,你将掌握 fmt-c C 接口、C++20 模块、编译期格式化(FMT_COMPILE / FMT_STATIC_FORMAT)、浮点格式化性能优化等关键能力的真实用法,以及从 8.0 升级到 12.2 时需要关注的破坏性变更。

版本脉络:12.2.1 与 12.2.0 的新特性速览

ChangeLog 顶部记录了最新的两个版本:12.2.1(TBD,尚未发布)12.2.0(2026-06-16)。当前仓库源码对应的正是这两个版本的状态。

12.2.0:C11 格式化 API(fmt-c)正式落地

12.2.0 最重磅的变更,是新增了面向 C 语言的快速、类型安全格式化接口。全新的fmt-c库与 include/fmt/fmt-c.h 头文件,借助 C11 的_Generic关键字按参数类型进行分发,性能优于printf/sprintf。ChangeLog 中给出的最小示例:

#include <fmt/fmt-c.h> fmt_print(stdout, "The answer is {}.\n", 42);

从 include/fmt/fmt-c.h 源码可以看到其底层设计:

  • 参数类型枚举fmt_type:定义了fmt_intfmt_uintfmt_boolfmt_charfmt_floatfmt_doublefmt_long_doublefmt_cstringfmt_pointer等类型标签;
  • 参数包装结构fmt_arg:通过type字段与union value承载不同类型的具体数值,指针则复用uint_valuepointer成员;
  • _Generic分派宏FMT_MAKE_ARG:把int/long/short/char等 C 内置类型自动映射到对应的fmt_from_*构造函数,并把不支持的参数类型派发到fmt_unsupported_type报错;
  • 变参包装宏FMT_FORMAT_ARGS/FMT_MAKE_ARGLIST:利用FMT_NARGFMT_MAP系列宏统计参数个数(上限 16 个),把实参展开为fmt_arg[]数组,最终调用底层入口fmt_vformatfmt_vprint

两个 C 入口函数的实现位于 src/fmt-c.cc:

  • fmt_vformat(char* buffer, size_t size, const char* fmt, const fmt_arg* args, size_t num_args):内部把fmt_arg数组逐一转换为fmt::basic_format_arg<fmt::format_context>,再委托给fmt::vformat_to_n写入用户缓冲区,返回写入的字符数;参数个数超过 16 或出现未知类型时返回fmt_error_invalid_arg(-2),格式化过程抛出异常时返回fmt_error(-1);
  • fmt_vprint(FILE* stream, ...):把参数转换后委托给fmt::vprint直接向FILE*流输出。

需要特别注意的是,ChangeLog 指出 fmt-c 依赖MSVC 2019+ 且必须开启/Zc:preprocessor标志(见 include/fmt/fmt-c.h 中的#error检查),原因是该接口依赖符合标准的预处理器来展开_Generic与可变参数宏。

此外 12.2.0 还包含以下值得关注的点:

  • 新增独立的fmt::fmt-moduleCMake 目标用于 C++20 模块构建,并配套了 CI 工作流;
  • 默认启用完整的 Dragonbox 查找表缓存(除非以二进制体积为优化目标,即定义了__OPTIMIZE_SIZE__),浮点格式化获得约 10%–25% 的加速;
  • std::unexpected支持格式化,fmt::println新增接受fmt::text_style的重载,fmt::printf支持把位置参数用作宽度与精度说明符;
  • fmt::detail::named_arg提升为公开 APIfmt::named_argstd::byte的 formatter 从fmt/format.h迁移到fmt/std.h
  • <fmt/core.h>默认与<fmt/base.h>等价,依赖旧行为(core.h 间接引入 format.h)的代码必须显式包含<fmt/format.h>或定义FMT_DEPRECATED_HEAVY_CORE
  • 移除了fmt::say,弃用了std::initializer_list版本的fmt::join与数组版本的fmt::vformat_to
  • 修复合入嵌套异常(std::throw_with_nested)的展开,pipe 写端关闭时的挂起/断言,以及c演示类型对越界整数的格式化问题。

12.2.1(TBD):面向异常与模块的小幅增强

仍在开发中的 12.2.1 集中了以下改进:

  • std::exception_ptr新增 formatter:格式化其引用的异常对象,为空时输出none
  • std::exceptionformatter 支持展开std::throw_with_nested创建的嵌套异常,各层之间以": "连接;
  • 从 C++20 模块导出fmt::basic_ostream_formatterfmt::ostream_formatterfmt::streamed
  • 文档补充了用 CMake 使用 C++20 模块(含fmt::fmt-module目标与编译器要求)的说明;
  • 128 位整数格式化优化:通过把值拆成 64 位分块而不是反复做 128 位除法,全范围取值时获得约 5 倍加速;
  • 修复了 chrono 格式化中超出范围的浮点时长、管道读端关闭导致的挂起、以及FMT_COMPILEformat_as用户自定义类型组合时的编译失败等问题;
  • CMake 构建树导出集不再受FMT_INSTALL影响,通过add_subdirectoryFetchContent引入 {fmt} 的项目也能导出依赖fmt::*的目标;fmt-c库开始像主库一样尊重BUILD_SHARED_LIBS

12.x 性能优化:数字背后的实现依据

ChangeLog 中多处给出了可复现的基准数据,这些优化点大多能在源码中找到对应证据。

浮点格式化:Dragonbox 与完整缓存

12.0.0 优化了默认浮点格式化:用格式字符串编译(format string compilation)写入栈上缓冲区的double格式化,比 11.2 快了 60% 以上(34.471ns → 21.000ns)。12.2.0 又默认启用了完整 Dragonbox 查找表缓存,平均每个double耗时降至 22.07ns,与sprintf(726.27ns)和ostringstream(864.34ns)相比优势明显。仓库 doc/perf.svg 也直观对比了ostringstreamostrstreamsprintfdoubleconvfmt在 "double to string" 场景下的耗时(时间越短越好),fmt 的条形最短。

Dragonbox 是 {fmt} 用于最短浮点表示的默认算法(Grisu 已在 10.0 中被替换),10.0 还为其增加了“给定精度”下的新算法。注意这些数据均来自特定平台(如 Apple M1 Pro + clang 17)的 dtoa-benchmark,实际数值会因平台与编译选项而异,结论请以自身环境实测为准。

编译期格式化与运行时分派

8.0.0 起默认开启编译期格式字符串检查:在支持 C++20consteval的编译器上(gcc 10+、clang 11+),fmt::print("{:d}", "I am not a number")会直接编译报错。需要运行时字符串时用fmt::runtime包裹:

fmt::print(fmt::runtime("{:d}"), "I am not a number");

8.0.0 同时引入FMT_COMPILE宏,把格式字符串编译为类型化代码,十六进制格式化"{:x}"提速 3–7 倍;_cf字面量则提供更简洁的写法(需要 gcc 9.3+ 对类类型作为非类型模板参数的支持):

#include <fmt/compile.h> using namespace fmt::literals; auto s = fmt::format(FMT_COMPILE("{}"), 42); // 宏写法 auto s = fmt::format("{}"_cf, 42); // UDL 写法

这些接口在 include/fmt/compile.h 中实现:FMT_COMPILE(s)在可用时展开为FMT_STRING_IMPL(s, fmt::compiled_string)operator""_cf则基于detail::fixed_string模板参数返回编译期字符串。

12.0.0 把编译期能力推到新高度:

  • fmt::format支持constexpr:在std::string支持 constexpr 的前提下,fmt::format(""_cf, 42)可在编译期完成;
  • FMT_STATIC_FORMAT:在编译期把格式化结果放进“尺寸恰好合适”的字符串中。ChangeLog 中的示例constexpr auto s = FMT_STATIC_FORMAT("{}", 42);编译后直接生成.asciiz "42",可通过s.c_str()s.str()访问。其实现位于 include/fmt/compile.h 第 594–600 行附近:用fmt::formatted_size(FMT_COMPILE(fmt_str), ...) + 1在编译期推算所需大小,再实例化静态存储的格式化结果。

配套地,9.1.0 让fmt::formatted_size支持编译期求值(constexpr size_t n = fmt::formatted_size("{}"_cf, 42);输出 2),9.0.0 让编译期浮点格式化不再依赖 header-only 模式。8.0.0 还要求用户自定义类型的formatter::formatconst成员函数,以与std::format保持一致——从源码结构看,这是 format string compilation 能够生成常量求值代码的前提。

其他 12.x 性能点

  • 12.1.0 优化buffer::append,在 spdlog 基准上带来最高约 16% 的提升;
  • 11.0.0 起fmt::print在常见场景直接写入 C 流缓冲区,减少动态内存分配,简单基准中从 10.x 的 63.8ns 降到 51.3ns;
  • 12.2.0 针对 back-insert iterator 采用批量append/insert(如std::vector<char>),调试构建二进制体积从约 200k 降到约 85k(bloat 测试),并在没有consteval时提升编译速度。

编译期检查与格式字符串的演进

默认开启的编译期检查与FMT_STRING

8.0.0 之前,编译期检查需要显式使用FMT_STRING宏;之后默认启用。12.2.0 进一步规定:当FMT_USE_CONSTEVAL启用时,FMT_STRING成为空操作(no-op),因为 consteval 格式字符串构造函数本身已提供编译期校验——也就是说,只要编译器支持,检查机制完全内建于类型系统中,无需宏参与。

具名参数与重复检测

11.2.0 修复了格式说明符中具名参数的处理,并新增重复具名参数的错误报告;8.0.0 为FMT_COMPILE增加 UDL 具名参数支持,参数在编译期解析、零运行时开销:

#include <fmt/compile.h> using namespace fmt::literals; auto s = fmt::format(FMT_COMPILE("{answer}"), "answer"_a = 42);

12.2.0 补齐了编译期格式字符串的具名参数校验(此前存在缺失),并把fmt::named_arg提升为公开 API。8.1.0 还实现了基于 UDL 的具名参数编译期检查:fmt::print("{answer:s}", "answer"_a=42)会在编译期报错,因为整数不支持s说明符。

格式字符串的 NUL 与 printf 语义

12.2.0 修复了编译期格式字符串检查在嵌入 NUL 字节时截断字符串字面量的问题;12.1.0 及之前版本还修复了printf格式化中的越界读取(12.2.0 的修复见 ChangeLog.md 相关条目)。12.0.0 移除了fmt::printf对格式字符串 NUL 结尾的假设,12.2.0 起fmt::printf支持位置参数作为宽度/精度说明符。printf相关实现集中在 include/fmt/printf.h,测试见 test/printf-test.cc。

新增 formatter 全家桶:标准库类型覆盖

ChangeLog 展示了 {fmt} 对标准库类型的持续覆盖,绝大多数 formatter 位于 include/fmt/std.h 与 include/fmt/ranges.h:

  • 异常体系:10.0.0 新增std::exceptionformatter(输出如vector<bool>::_M_range_check: ...),12.2.1 扩展到std::exception_ptr与嵌套异常展开;
  • std::expected:11.1.0 支持std::expected<void, E>(打印expected()),10.0.0 支持std::optional
  • chrono 日历类型:11.0.0 支持std::chrono::day/month/year/year_month_day,配合 include/fmt/color.h 可输出彩色日期;
  • 路径与文件系统:9.0.0 起支持std::filesystem::path,10.2.0 增加g表示(Windows 下C:\foo"C:/foo"),12.2.0 保证路径格式化无损(保留 ill-formed UTF-16 序列);
  • std::variant(9.0.0)、std::complex(11.0.0,并退出 tuple 格式化路径)、std::type_info(11.0.0)、std::source_location(10.2.0,输出file:line:col: function)、std::bitset(10.2.0,fmt::print("{}", std::bitset<6>(42));输出101010)、std::thread::id(9.0.0);
  • 容器适配器:10.0.0 支持std::priority_queuestd::queuestd::stack
  • 错误码:11.2.0 为std::error_code新增s说明符输出错误消息字符串(平台相关);11.1.0 为其增加宽度与对齐支持;
  • 代理引用:10.1.0 支持std::vector<bool>std::bitset<N>的代理引用元素。

格式化自定义类型的推荐路径也在演进:9.0.0 提供 ADL 查找的format_as扩展点(任何用户定义类型皆可用,不限于枚举),fmt::ostream_formatterfmt::streamed则用于复用operator<<。12.2.1 已把fmt::streamed从 C++20 模块中导出。

ranges 格式化与 fmt::join 的完善

范围格式化能力同样在历次版本中逐步增强(实现在 include/fmt/ranges.h):

  • 8.1.0 为字符串范围元素实现转义输出(["\naan"]),并切换到 JSON 风格的 map/set 表示({"answer": 42}),与 Pythonstr.format对齐;
  • 9.1.0 新增n说明符禁用分隔符输出:fmt::print("{:n}\n", std::vector{1, 2, 3});输出1, 2, 3
  • 9.0.0 支持嵌套说明符:fmt::print("{::#x}\n", std::vector{10, 20, 30});输出[0xa, 0x14, 0x1e]
  • 11.1.0 为 tuple 与 pair 新增n说明符,fmt::join支持 tuple-like 类型;12.2.0 为fmt::join支持wchar_t等非char分隔符,且弃用std::initializer_list重载;
  • 8.0.0 将范围分隔符从{}改为[],并移除了范围元素数量上限。

chrono 格式化:行为与语义的变迁

include/fmt/chrono.h 是历次版本的重点:

  • 默认本地化:8.0.0 起更多 chrono 说明符默认与 locale 无关,需显式L说明符启用本地化(如fmt::print("{:L}\n", monday);在 ru_RU 下输出пн);
  • UTC 语义:10.0.0 将std::chrono::system_clock格式化从本地时间改为 UTC,以对齐标准;11.2.0 修复了std::chrono::local_timetm的跨平台一致性,并为缺少时区信息的情况提供编译期诊断(fmt::print("{:Z}", std::chrono::local_seconds());直接编译报错);
  • 亚秒与精度:8.1.0 实现 chrono 时长亚秒格式化({:%S}配合std::chrono::milliseconds(1234)输出01.234),10.0.0 为%S增加精度支持并实现 subseconds 格式化,还修复了 epoch 之前时间点的输出(59.750000000);
  • 性能:8.1.0 优化 chrono 格式化,%z/%Y处理快 10–20 倍(261ns → 26.3ns);
  • 日期单位:10.2.0 为std::chrono::duration支持%j(天数)并修正单位后缀(std::chrono::days(42)输出42d,分钟后缀改为min);
  • 12.2.0 修复了 chrono 格式化中超出范围的浮点时长。

Unicode、宽度与本地化

  • 11.0.0 在 MSVC 上默认启用 Unicode 支持(可用 CMake 选项FMT_UNICODE控制),避免路径与终端输出出现乱码;
  • 12.0.0 起精度使用“估算显示宽度”:fmt::print("|{:.4}|\n|1234|\n", "🐱🐱🐱")因 emoji 估算宽度为 2 而输出 4 个位置;
  • 8.1.0 起fmt::group_digits提供非本地化的三位分组:fmt::print("{} dollars", fmt::group_digits(1000000));输出1,000,000 dollars
  • 8.0.0 允许L说明符与演示类型组合({0:.2Lf}在 fr_FR 下输出0,42),并移除了旧的n说明符;8.0.0 起默认浮点输出不再带尾随零({0:.3}对 1.1 输出1.1),保留尾随零需#
  • 12.2.0 修复了std::tmtm_zone为空时的格式化,并明确了十六进制浮点a/A始终输出0x前缀(见 include/fmt/format.h 相关文档)。

输出与文本样式:print、println、styled

  • 8.1.0 新增fmt::styled,对单个参数应用样式:fmt::styled("error", fg(fmt::color::red)),并支持 faint/conceal/reverse/blink 等文本样式(include/fmt/color.h);
  • 10.0.0 加入fmt::println(对齐 C++23),12.2.0 为其增加接受fmt::text_style的重载;
  • 11.0.0 起fmt::print可直接写入 C 流缓冲区,常见场景消除缓冲层动态分配;
  • 9.0.0 弃用通过fmt/ostream.h自动发现operator<<的行为(防止 ODR 违规),改用fmt::streamedfmt::ostream_formatterfmt::ostream_formatter的用法示例见 include/fmt/ostream.h 文档与 ChangeLog 9.0.0 条目(template <> struct fmt::formatter<date> : ostream_formatter {};)。

构建系统与兼容性要点

CMake 选项

从 CMakeLists.txt 可以看到当前仓库支持的构建选项:

选项默认值说明
FMT_DOCFMT_MASTER_PROJECT是否生成 doc 目标
FMT_INSTALLFMT_MASTER_PROJECT是否生成安装目标(作为子项目时默认关闭)
FMT_TESTFMT_MASTER_PROJECT是否生成测试目标
FMT_FUZZOFF是否生成 fuzz 目标
FMT_CUDA_TESTOFF是否生成 cuda-test 目标
FMT_OSON是否包含 OS 相关 API
FMT_MODULEFMT_USE_MODULES是否构建 C++20 module 库
FMT_SYSTEM_HEADERSOFF是否把 {fmt} 头文件标记为系统头(抑制警告)
FMT_UNICODEON是否启用 Unicode 支持
FMT_PEDANTICOFF启用额外警告与昂贵测试
FMT_WERROR-编译器警告按错误处理

其中FMT_UNICODE=OFF时定义FMT_UNICODE=0fmt-header-only目标默认定义FMT_HEADER_ONLY=1。12.0.0 将组件前缀从fmt-改为fmt_(如fmt-docfmt_doc),以兼容 Windows 上的 NSIS/CPack;11.1.1 定义了coredoc组件,允许单独安装文档。

头文件布局的破坏性变更

  • 12.2.0:<fmt/core.h>默认等价于<fmt/base.h>,不再隐式包含<fmt/format.h>(可用FMT_DEPRECATED_HEAVY_CORE恢复旧行为);std::byteformatter 移到fmt/std.h
  • 11.0.0:新增 include/fmt/base.h,提供最小包含依赖的 API 子集,可完全替代printf家族;100 个源文件的编译基准中,fmt 11.0 为 4.8s,而 10.x 为 19.0s(printf为 1.6s,IOStreams 为 25.9s);
  • 8.0.0:fmt/posix.h更名为fmt/os.hfmt/locale.h在 9.0.0 移除(locale 相关重载迁入fmt/format.hfmt/xchar.h);wchar_t专属 API 迁入fmt/xchar.h

破坏性移除清单(12.0.0)

  • has_formatter(改用is_formattable);
  • basic_format_args::parse_context_typeformatter_type等上下文类型别名;
  • 宽流版本的fmt::printf与带文本样式的宽流fmt::print
  • is_*chartraits 与fmt::localtime(改用std::localtime);
  • 宽流版本的fmt::fprintf/fmt::sprintf被弃用;
  • 9.0.0 移除的_formatarg_join、接收 memory buffer 的format_to重载等旧 API。

测试、安全与仓库配套资源

  • 仓库提供了覆盖上述特性的完整测试套件:test/format-test.cc、test/chrono-test.cc、test/ranges-test.cc、test/printf-test.cc、test/std-test.cc、test/xchar-test.cc 等;针对 C API 的测试位于 test/c-test.c。12.2.0 起 CI 还增加了 SLSA provenance 产物与 CodeQL 工作流、.github/SECURITY.md安全策略,并有 fuzzing 目标(test/fuzzing)覆盖 chrono duration/timepoint、浮点、具名参数等场景;
  • 文档源码位于 doc(含 doc/api.md、doc/syntax.md、doc/get-started.md),更早版本(0.8.0–7.1.3)的变更记录可查看 doc/ChangeLog-old.md;
  • 头文件可通过 include/fmt/format.h 直接包含(header-only 模式),或通过 CMake 链接fmt::fmt/fmt::fmt-header-only/fmt::fmt-module目标使用。

升级建议与迁移清单

  1. 若仍在使用fmt/core.h且依赖format.h的内容,请显式包含<fmt/format.h>,或临时定义FMT_DEPRECATED_HEAVY_CORE
  2. has_formatter替换为is_formattable,把fmt::localtime替换为std::localtime,移除is_*chartraits 的使用;
  3. 为枚举与自定义类型的隐式转换改用format_as;为需要operator<<格式化的类型改用fmt::streamedfmt::ostream_formatter
  4. 确保自定义 formatter 的format成员为const,以兼容编译期格式化与std::format语义;
  5. 检查宽流printf/fprintf/sprintf的使用,迁移到窄字符重载或fmt::print
  6. 若在 C 项目中需要类型安全格式化,可引入fmt-c库(#include <fmt/fmt-c.h>),注意 MSVC 下需/Zc:preprocessor
  7. 需要极致性能的场景,优先使用FMT_COMPILE/_cf编译期格式化,配合FMT_STATIC_FORMAT消除运行时分配;需要最小二进制体积时,避免定义__OPTIMIZE_SIZE__并评估FMT_USE_FULL_CACHE_DRAGONBOX相关缓存策略。

以上所有结论均可在 ChangeLog.md、include/fmt 头文件、src 实现与 test 测试中找到对应证据;具体性能数据依赖编译环境,请以官方 dtoa-benchmark 或自身基准为准。

【免费下载链接】fmtA modern formatting library项目地址: https://gitcode.com/GitHub_Trending/fm/fmt

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 14:32:29

SpringBoot冷链生鲜系统:温度监控与智能库存管理实践

1. 项目背景与核心价值冷链运输生鲜销售系统是当前生鲜电商和物流行业的核心基础设施之一。随着消费者对生鲜产品质量要求的不断提高&#xff0c;传统的常温运输方式已经无法满足高品质生鲜产品的配送需求。根据行业数据显示&#xff0c;2022年我国冷链物流市场规模已突破4000亿…

作者头像 李华
网站建设 2026/9/11 14:31:46

Paperxie 五大核心板块功能详解|从开题到答辩,关键环节全覆盖

很多同学用 Paperxie&#xff0c;只知道能写作和查重&#xff0c;却忽略了其他同样强大的功能板块。Paperxie 十大核心板块覆盖从开题到答辩的全流程&#xff0c;每个板块都精准对应毕设的一个关键环节&#xff0c;用好这些功能&#xff0c;论文效率直接翻倍。 今天随机挑选 5…

作者头像 李华
网站建设 2026/9/11 14:31:22

工业价格预测实战:避开数据口径陷阱与模型选型误区

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 14:29:34

DMA原理与实战:从嵌入式到SoC的硬件协处理器详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 14:26:10

AutoDL回收站清理:彻底释放GPU云服务器磁盘空间

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华