带有 printf 参数检查的可变参数模板

时间:2021-02-28 12:16:13

标签: c++ printf variadic-templates

我有一个 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' 指的是参数类型 '常量字符 (&)[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
  }

1 个答案:

答案 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++ 模板魔术或编译时格式字符串检查。

相关问题