我有一个 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):
<块引用>
<来源>:8:6: 错误:
'format' 属性参数 2 值 '1' 指的是参数类型
'常量字符 (&)[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
}
答案 0 :(得分:1)
您可以通过添加一个固定的 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++ 模板魔术或编译时格式字符串检查。