C++20编译时检测格式化字符串printf语法报错问题求助
编译时检查C++日志中的printf格式符
问题分析
你的核心需求是在C++上下文调用日志函数时,若使用%s/%i/%d这类printf风格格式符,直接触发编译错误。原代码存在几个关键问题:
- 检查逻辑错误:当前是判断整个字符串是否等于某个printf格式符,而非字符串包含这些格式符
- 检查时机错误:在格式化后的字符串上做检查,此时已经是运行时,无法触发编译错误
- consteval构造函数的异常抛出不符合常量表达式要求
- 头文件结构混乱(
#pragma once位置错误、重复包含)
修正后的实现
以下是修复后的完整代码,核心思路是在原始格式字符串传入日志函数时就进行编译时检查,利用consteval和字符串查找确保非法格式符无法通过编译:
#include <source_location> #include <string> #include <string_view> #include <fmt/format.h> #include <iostream> #include <algorithm> #include <array> #pragma once using namespace std::string_view_literals; // 需要拦截的printf风格格式符 constexpr auto printf_format_specifiers = std::array{ "%s"sv, "%i"sv, "%d"sv }; // 编译时字符串检查工具:确保输入字符串不包含任何printf格式符 struct CompileTimeFormatChecker { std::string_view fmt_str; consteval CompileTimeFormatChecker(std::string_view str) : fmt_str(str) { // 检查字符串中是否包含任何禁止的格式符 for (const auto& spec : printf_format_specifiers) { if (str.find(spec) != std::string_view::npos) { // 抛出常量表达式异常,触发编译错误 throw "C++日志禁止使用printf风格格式符,请使用{}类格式"; } } } // 隐式转换为fmt::format_string,兼容原有的fmt库逻辑 template <typename... Args> operator fmt::format_string<Args...>() const { return fmt::format_string<Args...>(fmt_str); } }; #ifdef __cplusplus // C++上下文:先经过编译时检查,再传给log函数 #define LOG_MESSAGE(fmt_str, ...) log(CompileTimeFormatChecker{fmt_str}, ##__VA_ARGS__) #else // C上下文:保留printf风格调用(仅作示例) #define LOG_MESSAGE(strLogMessage, ...) logFromC(strLogMessage, __FILE__, __LINE__, ##__VA_ARGS__) #endif void _log(std::string_view formatted_msg, const std::source_location loc = std::source_location::current()) { std::cout << formatted_msg << " | 位置: " << loc.file_name() << ":" << loc.line() << std::endl; } template <typename... Args> void log(CompileTimeFormatChecker fmt, Args&&... args) { std::string formatted_msg = fmt::format(static_cast<fmt::format_string<Args...>>(fmt), std::forward<Args>(args)...); _log(std::move(formatted_msg)); } int main() { std::string foo = "foo"; LOG_MESSAGE("foo {}", foo); // 正常编译 // LOG_MESSAGE("fail %s", foo); // 编译错误:触发禁止格式符检查 // LOG_MESSAGE("fail %s"); // 编译错误:触发禁止格式符检查 }
关键修复点
- 修正检查逻辑:遍历所有禁止的格式符,使用
string_view::find检查字符串是否包含这些格式符,而非判断字符串是否完全匹配 - 提前检查时机:通过
CompileTimeFormatChecker包装原始格式字符串,在宏展开时就触发编译时检查,而非等到格式化完成后 - 兼容fmt库:给
CompileTimeFormatChecker添加到fmt::format_string的隐式转换,确保不破坏原有的fmt格式化逻辑 - 规范头文件结构:将
#pragma once放在文件最开头,避免重复包含问题 - 合法的consteval异常:在consteval函数中抛出字符串字面量(常量表达式),确保编译时能正确触发错误
编译错误示例
当使用LOG_MESSAGE("fail %s", foo)时,编译器会输出类似错误:
error: expression '<throw-expression>' is not a constant expression
同时会显示自定义的错误提示信息,明确告知开发者需要使用fmt风格的格式符。
内容的提问来源于stack exchange,提问作者user8694474
相关产品推荐
相关产品推荐

