我有一个 printf 风格的函数,它接受可变数量的参数。这是我的出发点:
#include <stdio.h>
#include <stdarg.h>
void MyPrint (const char* fmt,...)
{
va_list arglist ;
va_start (arglist, fmt) ;
vprintf (fmt, arglist) ;
va_end (arglist) ;
}
int main()
{
MyPrint ("Hello, %s\n", "world") ;
}
这将按预期打印
Hello, world
。
现在我想做两个改变。首先,我想使用 g++ 的
format
属性检查格式字符串。所以我首先声明 MyPrint
函数(我必须先声明它,因为出于某种原因 g++ 不允许您为函数定义分配属性):
void MyPrint (const char* fmt,...) __attribute__ ((format (printf, 1, 2))) ;
现在如果我尝试例如
MyPrint ("Hello, %d\n", "world") ;
我收到一条很好的错误消息。
我想做的第二个更改是使用可变参数模板参数。像这样:
#include <utility> // for std::forward
template<typename...Params>
void MyPrint (Params&&... fmt)
{
printf (std::forward<Params> (fmt)...) ;
}
这也有效。因此,我将两者结合起来,通过使用此前向声明将格式检查属性添加到可变参数函数模板中:
template<typename...Params>
void MyPrint (Params&&... fmt) __attribute__ ((format (printf, 1, 2))) ;
但现在我收到此错误消息(gcc 10.2):
:替换 'template
void MyPrint(Params&& ...) [with Params = {const char (&)[11], const char (&)[6]}]':
:15:38:需要从这里
:8:6:错误: 'format' 属性参数 2 值 '1' 指参数类型 'const char (&)[11]'
这让我彻底困惑了。谁能告诉我我做错了什么?
这是完整的程序:
#include <stdio.h>
#include <utility> // for std::forward
template<typename...Params>
void MyPrint (Params&&... fmt) __attribute__ ((format (printf, 1, 2))) ;
template<typename...Params>
void MyPrint (Params&&... fmt) // <-- Line 8
{
printf (std::forward<Params> (fmt)...) ;
}
int main()
{
MyPrint ("Hello, %s\n", "world") ; // <-- Line 15
}
您可以通过添加固定的
const char *
参数作为格式字符串并将属性指向该参数来消除第一个错误。
template<typename...Params>
void MyPrint (const char * format, Params&&... fmt) __attribute__ ((format (printf, 1, 2))) ;
template<typename...Params>
void MyPrint (const char * format, Params&&... fmt) // <-- Line 9
{
printf (format, std::forward<Params> (fmt)...) ;
}
这揭示了另一个错误:
test.cc:8:6: error: ‘format’ attribute argument 3 value ‘2’ does not refer to a variable argument list
8 | void MyPrint (const char * format, Params&&... fmt) // <-- Line 9
| ^~~~~~~
似乎用于检查
printf
原型的属性依赖于一个 const char *
参数和变量参数列表,并且如果没有它们就不愿意工作。所以你必须放弃 C++ 模板魔法或编译时格式字符串检查。
仅供参考:这已在 gcc 13.1 中修复。该程序按预期工作,没有编译器错误或警告。