如何制作可变参数宏(可变数量的参数)
Posted
技术标签:
【中文标题】如何制作可变参数宏(可变数量的参数)【英文标题】:How to make a variadic macro (variable number of arguments) 【发布时间】:2010-10-15 08:45:01 【问题描述】:我想用 C 写一个宏,它接受任意数量的参数,而不是特定的数量
示例:
#define macro( X ) something_complicated( whatever( X ) )
其中X
是任意数量的参数
我需要这个,因为whatever
已重载,可以使用 2 或 4 个参数调用。
我尝试定义宏两次,但第二个定义覆盖了第一个!
我正在使用的编译器是 g++(更具体地说,是 mingw)
【问题讨论】:
你想要 C 还是 C++?如果您使用 C,为什么要使用 C++ 编译器进行编译?要使用正确的 C99 可变参数宏,您应该使用支持 C99(如 gcc)的 C 编译器而不是 C++ 编译器进行编译,因为 C++ 没有标准可变参数宏。 好吧,我认为 C++ 在这方面是 C 的超集.. tigcc.ticalc.org/doc/cpp.html#SEC13有可变参数宏的详细解释。 这里有一个很好的解释和例子http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html 对于未来的读者:C 不是 C++ 的子集。它们共享许多东西,但有一些规则阻止它们成为彼此的子集和超集。 【参考方案1】:• 可变数量的参数用省略号 (...) 表示 • ISO C 的语法要求在“...”之前至少有一个固定参数
例如,您可以输入:
#define DEBUGMSG ( int, ...)
【讨论】:
【参考方案2】:__VA_ARGS__
是执行此操作的标准方法。如果没有必要,不要使用特定于编译器的 hack。
我真的很生气,我不能对原来的帖子发表评论。无论如何,C++ 不是 C 的超集。用 C++ 编译器编译 C 代码真的很愚蠢。不要做唐尼不做的事。
【讨论】:
“用 C++ 编译器编译你的 C 代码真的很傻” => 不是每个人(包括我)都这么认为的。例如,请参阅 C++ 核心指南:CPL.1: Prefer C++ to C,CPL.2: If you must use C, use the common subset of C and C++, and compile the C code as C++。我很难想到一个人真正需要什么“纯 C 语言”才能使其不值得在兼容的子集中进行编程,并且 C 和 C++ 委员会一直在努力使该兼容的子集可用。 @HostileFork 很公平,尽管当然 C++ 人希望鼓励使用 C++。不过,其他人确实不同意。例如,Linux Torvalds 显然拒绝了多个提议的 Linux 内核补丁,这些补丁试图将标识符class
替换为 klass
以允许使用 C++ 编译器进行编译。另请注意,有些差异会让您感到困惑;例如,三元运算符在两种语言中的评估方式不同,inline
关键字的含义完全不同(正如我从另一个问题中了解到的那样)。
对于像操作系统这样的真正跨平台系统项目,你真的要遵守严格的 C,因为 C 编译器要常见得多。在嵌入式系统中,仍有一些平台没有 C++ 编译器。 (有些平台只有可以通过的 C 编译器!)C++ 编译器让我感到紧张,尤其是对于网络物理系统,我想我不是唯一有这种感觉的嵌入式软件/C 程序员。
@downbeat 无论您是否使用 C++ 进行生产,如果您关心的是严谨性,那么能够使用 C++ 编译为您提供了静态分析的魔力。如果您想查询 C 代码库...想知道某些类型是否以某种方式使用,学习如何使用 type_traits 可以为它构建有针对性的工具。你花大价钱买一个 C 的静态分析工具可以用一点 C++ 知识和你已经拥有的编译器来完成......
我说的是 Linux 的问题。 (我刚刚注意到上面写着“Linux Torvalds”哈!)【参考方案3】:
#define DEBUG
#ifdef DEBUG
#define PRINT print
#else
#define PRINT(...) ((void)0) //strip out PRINT instructions from code
#endif
void print(const char *fmt, ...)
va_list args;
va_start(args, fmt);
vsprintf(str, fmt, args);
va_end(args);
printf("%s\n", str);
int main()
PRINT("[%s %d, %d] Hello World", "March", 26, 2009);
return 0;
如果编译器不理解可变参数宏,您也可以使用以下任一方法去除 PRINT:
#define PRINT //
或
#define PRINT if(0)print
第一个退出 PRINT 指令,第二个阻止 PRINT 指令,因为条件为 NULL。如果设置了优化,编译器应该删除从未执行过的指令,例如: if(0) print("hello world");或 ((void)0);
【讨论】:
#define PRINT // 不会将 PRINT 替换为 // #define PRINT if(0)print 也不是一个好主意,因为调用代码可能有自己的 else-if 来调用 PRINT。更好的是:#define PRINT if(true);else print 标准的“什么都不做,优雅地”是 do while(0) 将代码结构考虑在内的正确的if
版本的“不要这样做”是:if (0) your_code else
在您的宏扩展终止else
之后的分号。 while
版本看起来像:while(0) your_code
do..while
版本的问题是do your_code while (0)
中的代码只执行一次,保证。在所有三种情况下,如果your_code
为空,则它是正确的do nothing gracefully
。【参考方案4】:
C99方式,VC++编译器也支持。
#define FOO(fmt, ...) printf(fmt, ##__VA_ARGS__)
【讨论】:
我认为 C99 在 VA_ARGS 之前不需要 ##。那可能只是 VC++。 ## 在 VA_ARGS 之前的原因是它吞下了前面的逗号,以防变量参数列表为空,例如。 FOO("a") 扩展为 printf("a")。这是 gcc(也许还有 vc++)的扩展,C99 至少需要一个参数来代替省略号。##
不需要并且不可移植。 #define FOO(...) printf(__VA_ARGS__)
以便携的方式完成工作; fmt
参数可以从定义中省略。
IIRC,## 是 GCC 特定的,允许传递零参数
##-syntax 也适用于 llvm/clang 和 Visual Studio 编译器。所以它可能不可移植,但主要编译器都支持它。【参考方案5】:
我认为这是不可能的,你可以用双括号来伪造它......只要你不需要单独的参数。
#define macro(ARGS) some_complicated (whatever ARGS)
// ...
macro((a,b,c))
macro((d,e))
【讨论】:
虽然可以使用可变参数宏,但使用双括号是一个不错的建议。 Microchip 的 XC 编译器不支持可变参数宏,所以这个双括号技巧是你能做的最好的。【参考方案6】:在这里解释了 g++,虽然它是 C99 的一部分,所以应该适用于所有人
http://www.delorie.com/gnu/docs/gcc/gcc_44.html
快速示例:
#define debug(format, args...) fprintf (stderr, format, args)
【讨论】:
GCC 的可变参数宏不是 C99 可变参数宏。 GCC 有 C99 可变参数宏,但 G++ 不支持它们,因为 C99 不是 C++ 的一部分。 其实g++会编译C++文件中的C99宏。但是,如果使用“-pedantic”编译,它将发出警告。 不是 C99。 C99 使用 VA_ARGS 宏)。 C++11 也支持__VA_ARGS__
,尽管早期版本的编译器也支持它们作为扩展。
这对 printf("hi"); 不起作用没有 var args 的地方。有什么通用的方法可以解决这个问题?以上是关于如何制作可变参数宏(可变数量的参数)的主要内容,如果未能解决你的问题,请参考以下文章