尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

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

fmt 在 C++14 旧编译器下如何用 FMT_STRING 启用编译期格式字符串检查 fmt 在 C14 旧编译器下如何用 FMT_STRING 启用编译期格式字符串检查【免费下载链接】fmtA modern formatting library项目地址: https://gitcode.com/GitHub_Trending/fm/fmt如果你的项目还停留在 C14或未启用 C20consteval的旧编译器fmt 默认的编译期格式字符串检查不会被触发——因为该检查在 doc/api.md 中说明是在支持 C20consteval的编译器上默认启用。这类编译器下对应的机制是 FMT_STRING 宏把格式字符串字面量包进FMT_STRING(...)检查就会在编译期完成而不是等到运行时抛出format_error。本文给出在 C14 环境下使用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为 0FMT_STRING(s)展开为FMT_STRING_IMPL格式字符串被构建成compile_string派生类型检查在编译期执行在支持consteval的新编译器上FMT_STRING(s)就是字面量本身检查由 C20 机制完成。FMT_USE_CONSTEVAL的判定逻辑在 fmt/core.h未定义__cpp_lib_is_constant_evaluated、语言标准低于 C20FMT_CPLUSPLUS 201709L即 C14/17 模式、GCC 10.2、Apple Clang 14 等情况下都会置为 0。因此 C14 编译环境下FMT_STRING必然走 legacy 路径。注意边界doc/api.md 明确FMT_STRINGrequires C14 or later and is a no-op in C11。如果你的工程只能编 C11这个宏不产生任何检查需要把编译标准提升到 C14 才有意义。准备 fmt 库按 doc/get-started.md 的任一方式准备即可本文不重复展开只列出与后续步骤直接相关的要点系统包安装Debian/Ubuntu 用apt install libfmt-devmacOS 用brew install fmt或conda install -c conda-forge fmtCMake 集成时链接fmt::fmt编译库或fmt::fmt-header-onlyheader-only 模式例如find_package(fmt)之后target_link_libraries(your-target fmt::fmt)编译选项保证 C 标准为 14 或更高如-stdc14这是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::vectorchar 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 Pythonsstr.formatdoc/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 中的 C20 示例fmt::format({:d}, I am not a number)gives a compile-time error in C20——同样的错配在 C20 下无需任何宏就能捕获而 C14 下必须包FMT_STRING才等价。判定标准很直接错误用例如上面的{:d} 字符串→ 编译期报错检查生效正确用例如FMT_STRING(The answer is {:d}), 42→ 正常编译运行后得到格式化结果。如果错误用例在你的工程里能编译通过说明该编译单元没有启用 C14宏退化为 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。适合在存量代码上一次性暴露所有未包装的格式字符串改完后再评估是否保留该宏。限制与边界C11 下无检查FMT_STRING在 C11 中是 no-op编译标准必须 ≥ C14。只接受字面量宏按string literal构造compile_string类型运行时格式字符串无法通过FMT_STRING获得编译期检查。C20 下宏是恒等展开FMT_USE_CONSTEVAL为 1 时FMT_STRING(s)直接展开为s检查转由 C20consteval路径完成行为与本文 C14 路径等效代码无需改动即可平滑过渡。【免费下载链接】fmtA modern formatting library项目地址: https://gitcode.com/GitHub_Trending/fm/fmt创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表