2014-09-03 173 views
0

我试图通过宏的多层次逗号,但预处理器不喜欢......错误尝试

// This is just simplified, in reality I have some other variadic function which will 
// stop upon encountering the -1 and APPEND_COMMA() may add more args (like `a, b,`) 

#include <stdio.h> 

#define MACRO_EX(val) printf("%d %d", val -1); 

#define MACRO(val) MACRO_EX(val) 

#define APPEND_COMMA(a) a, 

int main() { 
     MACRO(APPEND_COMMA(1)); 
     return 0; 
} 

到底是什么导致编译通过多个宏通逗号时在这里失败?

test.c:10:8: error: too many arguments provided to function-like macro invocation 
     MACRO(APPEND_COMMA(1)); 
      ^
test.c:7:26: note: expanded from macro 'APPEND_COMMA' 
#define APPEND_COMMA(a) a, 
         ^
test.c:5:29: note: expanded from macro 'MACRO' 
#define MACRO(val) MACRO_EX(val) 
          ^
test.c:3:9: note: macro 'MACRO_EX' defined here 
#define MACRO_EX(val) printf("%d %d", val 0); 
     ^
test.c:10:2: error: use of undeclared identifier 'MACRO_EX' 
     MACRO(APPEND_COMMA(1)); 
     ^
test.c:5:20: note: expanded from macro 'MACRO' 
#define MACRO(val) MACRO_EX(val) 
       ^
2 errors generated. 

逗号似乎在叫宏进行扩展,使MACRO_EX(a,)被调用,这不是我想要的。


我找到了一些办法解决这个使用__VA_ARGS__

#define MACRO_EX(...) printf("%d %d", __VA_ARGS__ -1); 

但我真的喜欢干净地通过逗号通过所有宏到可变参数函数调用,并使用干净命名宏参数[就像我在我的原始代码中打算的那样]。

有什么办法可以达到这个目的吗?

+0

在'gcc'编译器中尝试'-E'选项。 – 2014-09-03 14:20:28

+1

@ShafikYaghmour区别在于,逗号不是在宏调用中引入的,而是通过另一个宏引入的。这样我可以将它传递给'MACRO()',但不知何故它会被扩展。 – bwoebi 2014-09-03 14:20:46

+0

@ Don'tYouWorryChild nope,这是一个预处理器故障,它停在那里。如上所述,我知道发生了什么,我想有一种方法可以像参数那样传递它,而不是像__VA_ARGS __... – bwoebi 2014-09-03 14:22:04

回答

1

,以保证一个逗号将通过宏观参数“干净地”传递的唯一方法是把它包在括号:

#define APPEND_COMMA(a) (a,) 

现在的APPEND_COMMA结果将永远是一个预处理器的参数。

这个问题很明显,它产生了printf("%d %d", (1,) -1);,它现在是有效的预处理器代码,但不再有效的C代码。这是通过插入一个“解压”步骤进MACRO_EX定义修正:

#define IDENTITY(...) __VA_ARGS__ 
#define MACRO_EX(val) printf("%d %d", IDENTITY val -1); 

IDENTITY将解包的val的内容,假设val是parenthesised参数列表,在MACRO_EX的重新扫描步骤。

val可能因为不包含逗号而不能加括号的情况怎么办?答案是不要让它们出现。参数可能包含一个逗号应该总是被包装,所以他们总是可以安全地解开在最后插入点。宏的设计需要知道他们是否期望单个令牌或列表*。这是预处理器的静态类型约束的等价物:像MACRO这样的宏需要一个类型为list的参数,而不是类型atom(并且调用的宏类似地需要用正确的类型签名书写);在那些只需要单个参数的场合,将它们传递给单元素列表。

*在技术上可以设计一个宏来测试它的参数是否被括起来并在结果上分支,但是你在那里进入黑暗魔法领域。为了清晰起见,坚持固定类型。

+0

包装和展开绝对是一个不错的主意。谢谢你的提示。 – bwoebi 2014-09-03 22:55:06