news 2026/9/13 3:44:28

fmt 在 C++14 旧编译器下如何用 FMT_STRING 启用编译期格式字符串检查

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
fmt 在 C++14 旧编译器下如何用 FMT_STRING 启用编译期格式字符串检查

fmt 在 C++14 旧编译器下如何用 FMT_STRING 启用编译期格式字符串检查

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

如果你的项目还停留在 C++14(或未启用 C++20consteval的旧编译器),fmt 默认的编译期格式字符串检查不会被触发——因为该检查在 doc/api.md 中说明是"在支持 C++20consteval的编译器上默认启用"。这类编译器下对应的机制是 FMT_STRING 宏:把格式字符串字面量包进FMT_STRING(...),检查就会在编译期完成,而不是等到运行时抛出format_error。本文给出在 C++14 环境下使用FMT_STRING的操作路径,以及如何用FMT_ENFORCE_COMPILE_STRING强制所有调用点走编译期检查。

前提:确认你的编译走的是 legacy 检查路径

FMT_STRING在 fmt/format.h 中的定义取决于FMT_USE_CONSTEVAL

#if FMT_USE_CONSTEVAL # define FMT_STRING(s) s #else # define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string) #endif // FMT_USE_CONSTEVAL

也就是说,在旧编译器上(FMT_USE_CONSTEVAL为 0),FMT_STRING(s)展开为FMT_STRING_IMPL,格式字符串被构建成compile_string派生类型,检查在编译期执行;在支持consteval的新编译器上,FMT_STRING(s)就是字面量本身,检查由 C++20 机制完成。

FMT_USE_CONSTEVAL的判定逻辑在 fmt/core.h:未定义__cpp_lib_is_constant_evaluated、语言标准低于 C++20(FMT_CPLUSPLUS < 201709L,即 C++14/17 模式)、GCC < 10.2、Apple Clang < 14 等情况下都会置为 0。因此 C++14 编译环境下FMT_STRING必然走 legacy 路径。

注意边界:doc/api.md 明确FMT_STRING"requires C++14 or later and is a no-op in C++11"。如果你的工程只能编 C++11,这个宏不产生任何检查,需要把编译标准提升到 C++14 才有意义。

准备 fmt 库

按 doc/get-started.md 的任一方式准备即可,本文不重复展开,只列出与后续步骤直接相关的要点:

  • 系统包安装:Debian/Ubuntu 用apt install libfmt-dev,macOS 用brew install fmt,或conda install -c conda-forge fmt
  • CMake 集成时链接fmt::fmt(编译库)或fmt::fmt-header-only(header-only 模式),例如find_package(fmt)之后target_link_libraries(<your-target> fmt::fmt)
  • 编译选项保证 C++ 标准为 14 或更高(如-std=c++14),这是FMT_STRING生效的前提。

用 FMT_STRING 包装格式字符串

FMT_STRING定义在fmt/format.h中,宏的文档注释给出了它的作用——"Constructs a legacy compile-time format string from a string literals",见 include/fmt/format.h。使用方式就是把格式字符串字面量直接包一层:

#include <fmt/format.h> int main() { // 检查在编译期完成 std::string s = fmt::format(FMT_STRING("The answer is {:d}"), 42); }

fmt 官方测试 test/enforce-checks-test.cc 覆盖了各 API 的典型写法,可照搬为检查清单:

#include <iterator> #include <vector> #include <fmt/format.h> void test() { // fmt::format / fmt::print 等格式化函数 (void)fmt::format(FMT_STRING("{}"), 42); // 宽字符串字面量同样支持 (void)fmt::format(FMT_STRING(L"{}"), 42); // fmt::format_to std::vector<char> out; fmt::format_to(std::back_inserter(out), FMT_STRING("{}"), 42); // fmt::format_to_n char buffer[4]; fmt::format_to_n(buffer, 3, FMT_STRING("{}"), 12345); }

两点限制需要注意:

  • FMT_STRING接受的是字符串字面量(宏实现中通过s[0]推导字符类型,见 FMT_STRING_IMPL),不能把运行时拼接的std::string塞进去;
  • 多余参数是允许的,"Unused arguments are allowed as in Python'sstr.format"(doc/api.md 的 Compile-Time Checks 一节)。

验证:用一条错误的格式字符串确认检查生效

验证方式就是"故意写错,看编译器是否报错"。宏文档注释给出的示例正是这个场景:

// A compile-time error because 'd' is an invalid specifier for strings. std::string s = fmt::format(FMT_STRING("{:d}"), "foo");

{:d}是整数说明符,传给字符串字面量"foo",预期结果是编译失败而不是运行期异常。对比一下:README 中的 C++20 示例fmt::format("{:d}", "I am not a number")"gives a compile-time error in C++20"——同样的错配在 C++20 下无需任何宏就能捕获,而 C++14 下必须包FMT_STRING才等价。

判定标准很直接:

  • 错误用例(如上面的{:d}+ 字符串)→ 编译期报错,检查生效;
  • 正确用例(如FMT_STRING("The answer is {:d}"), 42)→ 正常编译,运行后得到格式化结果。

如果错误用例在你的工程里能编译通过,说明该编译单元没有启用 C++14(宏退化为 no-op),或者没有包含fmt/format.h中定义该宏的编译路径。

可选:用 FMT_ENFORCE_COMPILE_STRING 强制所有调用点

只靠自觉包FMT_STRING容易被漏。doc/api.md的 Legacy Compile-Time Checks 一节提供了开关:

// 必须在包含 fmt 头文件之前定义 #define FMT_ENFORCE_COMPILE_STRING #include <fmt/format.h> int main() { fmt::print("hello {}\n", 42); // 编译失败:普通字符串不被接受 fmt::print(FMT_STRING("hello {}\n"), 42); // 通过 }

设置后,"functions acceptingFMT_STRINGwill fail to compile with regular strings"。实现上的强制点在 fmt/core.h 的enforce_compile_checks:违反时触发static_assert,信息为FMT_ENFORCE_COMPILE_STRING requires format strings to use FMT_STRING。适合在存量代码上一次性暴露所有未包装的格式字符串,改完后再评估是否保留该宏。

限制与边界

  • C++11 下无检查FMT_STRING在 C++11 中是 no-op,编译标准必须 ≥ C++14。
  • 只接受字面量:宏按string literal构造compile_string类型,运行时格式字符串无法通过FMT_STRING获得编译期检查。
  • C++20 下宏是恒等展开FMT_USE_CONSTEVAL为 1 时FMT_STRING(s)直接展开为s,检查转由 C++20consteval路径完成,行为与本文 C++14 路径等效,代码无需改动即可平滑过渡。

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

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

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

OpenCode实战指南:终端AI编程助手的安装配置与高效玩法

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

作者头像 李华
网站建设 2026/9/13 3:31:26

Content-Type详解:请求头、响应头、编码与文件上传实战指南

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

作者头像 李华
网站建设 2026/9/13 3:31:19

LocalAI 加载模型失败并提示 grpc service not ready 怎么排查?

LocalAI 加载模型失败并提示 grpc service not ready 怎么排查&#xff1f; 【免费下载链接】LocalAI LocalAI is the open-source AI engine. Run any model - LLMs, vision, voice, image, video - on any hardware. No GPU required. 项目地址: https://gitcode.com/GitHu…

作者头像 李华