如何为可变参数模板函数获取类printf格式警告?
问题:让模板包装的格式化函数获得printf风格的编译警告
1. 直接调用printf的正常警告
用g++ -Wformat编译以下代码时,会因%lld和int类型的argc不匹配,输出预期的格式警告:
int main(int argc, char * argv[]) { printf("argc %lld\n", argc); }
2. 可变参数模板包装后无警告
将printf包装在可变参数模板函数中后,再用同样的编译选项编译,不会触发任何格式不匹配警告,即使去掉引用也无法解决:
template <typename... Ts> void foo(char const * s, Ts && ... ts) { printf(s, std::forward<Ts>(ts)...); } int main(int argc, char * argv[]) { foo("argc %lld\n", argc); }
3. 使用[[gnu::format]]注解模板函数报错
尝试用C++17的[[gnu::format(printf,1,2)]]注解模板函数时,编译器抛出错误:
error: 'format' attribute argument 3 value '2' does not refer to a variable argument list
4. 实际需求
需要实现一个如下签名的模板函数,要求:
- 保留参数的类型信息(因此不能退化为C风格可变参数)
- 获得和直接调用printf完全一致的格式匹配编译警告
- 不使用宏实现
template <typename T, typename... ArgTs> void format(T t, char const * format_str, ArgTs && ... args);
解决方案
针对GCC/Clang编译器,可以利用其扩展的format属性,针对模板参数包调整属性的参数:
将模板函数标注为[[gnu::format(printf, 2, 0)]],其中:
printf表示按照printf的格式规则检查2表示格式字符串是函数的第2个参数(从1开始计数)0表示可变参数部分是模板参数包,而非C风格的va_list或可变参数(...)
最终代码如下:
#include <utility> template <typename T, typename... ArgTs> [[gnu::format(printf, 2, 0)]] void format(T t, char const * format_str, ArgTs && ... args) { // 这里可以添加自定义逻辑,比如使用t做一些处理 printf(format_str, std::forward<ArgTs>(args)...); } int main(int argc, char * argv[]) { format(0, "argc %lld\n", argc); // 编译时会触发和直接调用printf一样的格式警告 }
用g++ -Wformat编译上述代码,会正确输出%lld与int不匹配的警告,同时保留了模板参数的类型信息,也没有使用宏。
内容的提问来源于stack exchange,提问作者Jody Hagins
相关产品推荐
相关产品推荐

