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

问题描述 投票:0回答:2

我有一个 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
  }
c++ printf variadic-templates
2个回答
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++ 模板魔法或编译时格式字符串检查。


0
投票

仅供参考:这已在 gcc 13.1 中修复。该程序按预期工作,没有编译器错误或警告。

© www.soinside.com 2019 - 2024. All rights reserved.