使用C11和-Wno-variadic-macros时,ISO C99要求可变宏中的“…”至少有一个参数。

12
我有一个简单的宏定义:#define log(text, ...) fprintf(stderr, "stuff before" text "stuff after", ## __VA_ARGS__);,会触发错误:error: ISO C99 requires at least one argument for the "..." in a variadic macro [-Werror] 使用-std=c11-Wno-variadic-macros是否可以修复此错误/警告?
在定义log之前的头文件中添加#pragma GCC system_header可以解决此问题(尚未测试输出的二进制文件是否有效...),但这似乎有点hacky,而且我不确定其后果。
以下是预期行为的示例:https://dev59.com/FmIj5IYBdhLWcg3wGRiM#31327708 来自GNU的链接:https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
-Wvariadic-macros

    Warn if variadic macros are used in ISO C90 mode, or if the GNU alternate syntax is used in ISO C99 mode.
    This is enabled by either -Wpedantic or -Wtraditional.
    To inhibit the warning messages, use -Wno-variadic-macros.

有没有一种优雅的解决方案来阻止这个法定GNU GCC C代码的警告/错误?为什么它说我正在使用C99,为什么禁用C99警告的标志不起作用?行看起来像:

gcc -c src/file.c -Wall -Werror -Wextra -pedantic -Wfloat-equal -Wwrite-strings -Wcast-qual -Wunreachable-code -Wcast-align -Wstrict-prototypes -Wundef -Wshadow -Wstrict-aliasing -Wstrict-overflow -Wno-variadic-macros -g3 -std=c11 -O2 -flto -Iinclude/ -MMD -MF depend/file.d -o bin/file.o

请注意,-pedantic确实是罪魁祸首。



MCVE



c.c

#include <stdio.h>
#include <stdlib.h>

#define log(text, ...) fprintf(stderr, "stuff before" text "stuff after", ## __VA_ARGS__);

int main(void)
{

    log("should work, but doesn't");
    log("works fine: %s", "yep");

    return EXIT_SUCCESS;
}

Makefile

all:
    gcc c.c -Wall -Werror -Wextra -pedantic -Wfloat-equal -Wwrite-strings -Wcast-qual -Wunreachable-code -Wcast-align -Wstrict-prototypes -Wundef -Wshadow -Wstrict-aliasing -Wstrict-overflow -Wno-variadic-macros -g3 -std=c11 -O2

注意:移除pedantic可以编译成功 - gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609

#define log(text, ...) fprintf(stderr, "stuff before" text "stuff after", ## __VA_ARGS__); 是一个麻烦的宏定义。如果 text 不是字符串字面量,那么它就会出错,这不符合 printf 风格调用的预期。 - Andrew Henle
你为什么要阅读GCC 2.95.2的文档?那已经过时了。 - aschepler
那部分并不一定重要。你并不是第一个编写可变参数日志宏的人。如果你认为我说的不重要,那么你就错过了与你的问题的联系。一旦你解决了这个问题,你也会得到一个宏,在几个月甚至几年后看起来更有意义... - Andrew Henle
1
规范在C99和C11中是相同的。可能gcc没有费心根据您的标志更改错误消息。您可以报告错误,尽管我怀疑是否有人会在意花时间添加此功能... - M.M
1
@jake:你应该重新阅读一下-Wpedantic-pedantic是相同的,但因为明显的原因已被弃用)的实际含义。 - too honest for this site
显示剩余5条评论
2个回答

13
在ISO C99和C11中,当您定义一个宏时,例如:
#define log(text, ...)   something

如果要调用该宏,则必须至少传入2个参数。您的代码在ISO C(所有版本)中都是不合法的。

根据文档,GCC标志-pedantic表示:

发出符合严格ISO C和ISO C ++要求的所有警告;拒绝使用禁止扩展功能的所有程序以及一些不遵循ISO C和ISO C ++的程序。对于ISO C,请遵循所使用的任何-std选项指定的ISO C标准版本。

GCC开发人员决定在“不遵循ISO C”的“其他一些程序”下包含使用此扩展名的代码。如果您想在代码中使用此非标准扩展名,则不应使用-pedantic标志。

如果您请求C11一致性,则GCC开发人员也没有费心修改错误消息的文本以说“ISO C11禁止...”。如果这让您感到担忧,那么可以提交一个补丁。


关于-Wno-variadic-macros,文档如下:

在ISO C90模式下使用变参宏或在ISO C99模式下使用GNU替代语法时发出警告。

根据GCC文档(而不是提供少于最小数量的扩展名),“GNU替代语法”似乎指的是在C99之前启用变参宏的GNU语法:

GCC has long supported variadic macros, and used a different syntax that allowed you to give a name to the variable arguments just like any other argument. Here is an example:

#define debug(format, args...) fprintf (stderr, format, args)

我不理解这个警告的意思:warning: ISO C++11 requires at least one argument for the "..." in a variadic macro。难道在__VA_ARGS__之前的##不应该在未使用逗号的情况下删除它吗?但是,即使如此,-pedantic标志也会输出这个警告。 - Tesla123
@Tesla123,“删除逗号”行为是GNU扩展,不是标准的一部分。 - M.M

4
这是C99的解决方案:
#define debug_print(...) \
        do { fprintf(stderr, "%s:%d:%s(): ",__FILE__, __LINE__, __func__);\
             fprintf(stderr, __VA_ARGS__); } while (0)

我告诉你,超越限制的思维可以为你带来很多好处。只需移除显式的格式参数,让它在可变参数中隐含即可。为什么我没想到这个主意呢?我一直专注于设计一个合适的接口。 - undefined
在创建一个合适的界面方面,我会对你的回答进行以下改进:#define debug_print(/fmt,/...) do fprintf(stderr,/fmt,/...) while(0)。在这里,我们展示了我们使用了一个 fmt 字符串,但我们将其注释掉了。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接