【问题标题】:How to get printf style compile-time warnings or errors如何获得 printf 风格的编译时警告或错误
【发布时间】:2010-06-23 20:07:06
【问题描述】:

我想编写一个类似 printf 的例程,但不是在功能上,而是我希望该例程与 printf 具有相同的编译检查特性。

例如,如果我有:

{
   int i;
   std::string s;
   printf("%d %d",i);
   printf("%d",s.c_str());
}

编译器这样抱怨:

1 cc1plus: warnings being treated as errors
2 In function 'int main()':
3 Line 8: warning: too few arguments for format
4 Line 9: warning: format '%d' expects type 'int', but argument 2 has type 'const char*'

code example

printf 和 co 是编译器区别对待的特殊函数,还是有一些技巧可以让它在任何用户定义的函数上工作?我感兴趣的具体编译器是 gcc 和 msvc

【问题讨论】:

    标签: c++ compiler-construction printf typechecking


    【解决方案1】:

    不同的编译器可能会以不同的方式实现此功能。在 GCC 中,它是通过带有 format 属性的 __attribute__ 说明符实现的(阅读它 here)。编译器执行检查的原因只是在 GCC 提供的标准头文件中,printf 函数声明为 __attribute__((format(printf, 1, 2)))

    以完全相同的方式,您可以使用format 属性将相同的格式检查功能扩展到您自己的可变参数函数,这些函数使用与printf 相同的格式说明符。

    只有当您使用的参数传递约定和格式说明符与标准 printfscanf 函数使用的相同时,这一切才有效。检查被硬编码到编译器中。如果您对可变参数传递使用不同的约定,编译器将不会帮助您检查它。

    【讨论】:

    • 它可以做的比 printf 和 scanf 多一点;当前文档中的列表是“printf、scanf、strftime、gnu_printf、gnu_scanf、gnu_strftime 或 strfmon”
    • 这很酷。我希望 CodeGear/Embarcadero 将来能把这个特性放到他们的编译器中。
    【解决方案2】:

    此行为高度依赖于编译器。我相信gcc provides an interface for type checking variadic functions

    【讨论】:

      【解决方案3】:

      printf() 和朋友们并不特别,因为他们接受可变数量的参数:用户定义的函数也可以接受可变数量的参数。它们之所以特殊,是因为它们的行为是由标准定义的,因此编译器知道格式字符串和传递给函数的参数之间的相关性。

      实际上,编译器知道在调用函数时传递了多少参数,因此它会解析格式字符串,并将预期的参数数量和类型与实际传递给函数的参数进行比较,并在出现以下情况时发出警告他们不匹配。

      如果您使用 C++,我会避免编写您自己的可变参数函数;在大多数项目中使用它们的充分理由很少。例如,如果您要进行格式化,请使用流或像 Boost Format 这样的库。任何可以使用可变参数函数解决的问题都可以使用非可变参数函数来解决,而且在几乎所有情况下,结果都更加优雅、惯用且类型安全。

      【讨论】:

      • 在使用 C++ 编码时,实际上有很多充分的理由可以避免使用流和 Boost,但它们都是特定于领域的。我们中的大多数人都在做一些特定领域的工作,所以我们应该尽量不要忘记这样一个事实,即许多 C++ 功能对很多人来说都太昂贵了。
      • @dash-tom-bang:是的,你是对的:在某些场景中它们肯定是有用的。
      【解决方案4】:

      实际上printf 根本没有任何固有的编译时安全性。碰巧一些最近的编译器已经实现了特殊检查,因为它们确切地知道格式字符串在附加参数方面的含义。当您使用... 作为参数时,您表示您希望接受任意参数并承担确保它们正确的全部责任。编译器无法检查它们的计数/类型安全性。

      不要试图让编译器以这种方式帮助您,而是尝试使用标准流使用的方法:使用(可能是模板)函数或返回对 this 的引用的运算符以允许链接。然后编译器将能够在参数与预期/支持的不匹配时立即告诉您。

      【讨论】:

        【解决方案5】:

        不久前,有人向 boost 组发布了一个 mpl::string。我想它可能实际上已经进入了图书馆。如果是这种情况,您可以通过提供模板字符串作为模板参数(一个 mpl::string)然后使用一些非常深刻的元编程技能来解析其中的格式位来实现类似的功能。然后,您将使用此信息来选择具有适当参数计数和类型的实现。

        不,我不会为你做这件事:P 这会很困难。不过,我相信这是可能的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-19
          • 2011-01-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-15
          • 1970-01-01
          相关资源
          最近更新 更多