如何实现my_formatter_checker:基于字符串字面量编译期选函数
实现方案
1. 格式字符串检查函数(核心逻辑)
先实现区分fmt风格({})和printf风格(%开头)的检查函数,同时支持编译期字面量和运行时字符串:
编译期检查实现
利用constexpr特性扫描字符串字面量,跳过转义的{{}},判断是否包含有效fmt占位符:
// 核心编译期扫描逻辑 constexpr bool is_fmt_format_string_impl(const char* str) { while (*str != '\0') { if (*str == '{') { // 跳过转义的{{ if (*(str + 1) == '{') { str += 2; continue; } // 找到非转义的{,判定为fmt格式 return true; } else if (*str == '}') { // 跳过转义的}} if (*(str + 1) == '}') { str += 2; continue; } } ++str; } // 未找到fmt占位符,判定为printf格式 return false; } // 匹配编译期字符串字面量(const char[N]) template <size_t N> constexpr bool my_formatter_checker(const char (&fmt)[N]) { return is_fmt_format_string_impl(fmt); } // 匹配运行时字符串(const char*),强制走运行时分支 constexpr bool my_formatter_checker(const char* fmt) { return false; }
运行时检查实现
针对运行时生成的字符串,在运行时执行扫描判断:
bool my_formatter_checker_runtime(const char* fmt) { while (*fmt != '\0') { if (*fmt == '{') { if (*(fmt + 1) == '{') { fmt += 2; continue; } return true; } else if (*fmt == '}') { if (*(fmt + 1) == '}') { fmt += 2; continue; } } ++fmt; } return false; }
2. 参数转发的底层函数
针对两种调用形式(带输出目标/不带输出目标)实现重载,根据检查结果转发到对应的fmt库函数:
不带输出目标的版本
#include <fmt/core.h> #include <fmt/printf.h> #include <utility> // 编译期字符串分支:编译期确定调用fmt::print或fmt::printf template <size_t N, typename... Args> void print_impl(const char (&fmt)[N], Args&&... args) { if constexpr (my_formatter_checker(fmt)) { fmt::print(fmt, std::forward<Args>(args)...); } else { fmt::printf(fmt, std::forward<Args>(args)...); } } // 运行时字符串分支:运行时检查后选择对应函数 template <typename... Args> void print_impl(const char* fmt, Args&&... args) { if (my_formatter_checker_runtime(fmt)) { fmt::print(fmt, std::forward<Args>(args)...); } else { fmt::printf(fmt, std::forward<Args>(args)...); } }
带输出目标的版本
// 编译期字符串+输出目标分支 template <typename Stream, size_t N, typename... Args> void print_impl(Stream&& stream, const char (&fmt)[N], Args&&... args) { if constexpr (my_formatter_checker(fmt)) { fmt::print(std::forward<Stream>(stream), fmt, std::forward<Args>(args)...); } else { fmt::printf(std::forward<Stream>(stream), fmt, std::forward<Args>(args)...); } } // 运行时字符串+输出目标分支 template <typename Stream, typename... Args> void print_impl(Stream&& stream, const char* fmt, Args&&... args) { if (my_formatter_checker_runtime(fmt)) { fmt::print(std::forward<Stream>(stream), fmt, std::forward<Args>(args)...); } else { fmt::printf(std::forward<Stream>(stream), fmt, std::forward<Args>(args)...); } }
3. 封装用户调用宏
用宏封装底层函数,保持用户调用方式完全不变:
#define MY_PRINT(...) print_impl(__VA_ARGS__)
验证示例
基础调用场景
int x = some_runtime_func(); // 运行时求值为20 float y = some_runtime_func(); // 运行时求值为10.5 MY_PRINT("{} {}", x, y); // 编译期检测fmt格式,调用fmt::print MY_PRINT("%d %f", x, y); // 编译期检测printf格式,调用fmt::printf
带输出目标场景
int x = runtime_function(); // 返回20 MY_PRINT(stderr, "{}", x); // 编译期检测fmt格式,输出到stderr MY_PRINT(stderr, "%d", x); // 编译期检测printf格式,输出到stderr
关键特性说明
- 编译期安全:字符串字面量的检查完全在编译期完成,无运行时开销,且运行时参数(如x、y)不会在编译期被求值。
- 自动类型区分:通过模板重载自动识别编译期字符串(
const char[N])和运行时字符串(const char*),运行时字符串自动走运行时检查分支。 - 完全兼容fmt库:支持fmt库的输出目标参数(如
stderr、文件流),调用方式与fmt原生接口一致。
内容的提问来源于stack exchange,提问作者Nelsen Edbert Winata
相关产品推荐
相关产品推荐

