我有一个看起来像这样的宏:
M(id,...)
如果id == 0
以及其他内容,我希望它扩展为空。
这可能吗?如果是这样,怎么样?
我的第一直觉是尝试这样的事情:
#define M(id,...) M##id(__VA_ARGS__)
#define M0(...)
#define M_NOT_0(...) some_code(__VA_ARGS__)
但是这里的最后一行显然无效,我无法找到使这种模式有效的方法。
注意:
id
是介于0到255之间的整数,但理想情况下,我希望避免创建256个单独的宏定义。M(id,...)
宏本身无法更改。答案 0 :(得分:5)
宏CHECK0根据宏中遇到的令牌数量工作。
如果传递给宏的标记值为0,则会扩展为HIDDEN0,扩展为两个标记。否则它会扩展为不存在的宏,并且此宏被视为一个令牌。
宏SECOND然后选择第二个令牌。如果遇到HIDDEN0,则选择0,如果遇到不存在的令牌,则选择1.
然后,该结果与用户选择的前缀相结合。这可以在宏HELLO和PRINT中看到。一个是普通的宏,另一个是宏功能。生成的宏是HELLO0或HELLO1。一个被定义为有用的东西,另一个被定义为空。 PRINT也是如此。
#include <stdlib.h>
#include <stdio.h>
#define EXPAND(...) __VA_ARGS__ //needed for MSVC compatibility
#define JOIN_EXPAND( a , b ) a##b
#define JOIN( a , b ) JOIN_EXPAND( a , b )
#define SECOND_EXPAND( a , b , ... ) b
#define SECOND(...) EXPAND( SECOND_EXPAND( __VA_ARGS__ ) )
#define HIDDEN0 unused,0
#define CHECK0( value ) SECOND( JOIN( HIDDEN , value ) , 1 , unused )
#define HELLO0 puts( "HELLO" )
#define HELLO1
#define HELLO( value ) JOIN( HELLO , CHECK0( value ) )
#define PRINT0( ... ) printf( __VA_ARGS__ )
#define PRINT1( ... )
#define PRINT( value , ... ) JOIN( PRINT , CHECK0( value ) )( __VA_ARGS__ )
int main( void )
{
HELLO( 54545 ) ; //evaluates to nothing
HELLO( 1 ) ; //evaluates to nothing
HELLO( 0 ) ; //evaluates to puts( "HELLO" )
PRINT( 861151 , "Some values: %lf %d\n" , 3.13 , 12345 ) ; //evaluates to nothing
PRINT( 1 , "Some values: %lf %d\n" , 3.13 , 12345 ) ; //evaluates to nothing
PRINT( 0 , "Some values: %lf %d\n" , 3.13 , 12345 ) ; //evaluates to printf( "Som
printf( "%d %d %d\n", CHECK0( 0 ) , CHECK0( 1 ) , CHECK0( 123456 ) ) ; //outputs 0 1 1
return EXIT_SUCCESS ;
}
答案 1 :(得分:4)
几乎完全基于@PaulFultzII对this question的非常详细的回答,这是一种方法:
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t
#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)
#define PROBE(x) x, 1
#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0
#define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x))
#define NOT_0 PROBE(~)
#define BOOL(x) COMPL(NOT(x))
#define IF(c) IIF(BOOL(c))
#define EAT(...)
#define EXPAND(id, ...) printf(__VA_ARGS__, id)
#define M(id, ...) IF(id)(EXPAND(id, __VA_ARGS__), EAT(id, __VA_ARGS__))
M(0, "don't ever print this!\n")
M(1, "ok to print %s, %d\n", "with a string")
M(172, "ok to print %d\n")
如果我们通过预处理器(在GNU C编译器的情况下为cpp
)运行它,我们得到这个:
printf("ok to print %s, %d\n", "with a string", 1)
printf("ok to print %d\n", 172)
答案 2 :(得分:0)
some_code()
实际上是代码这可能很棘手:如果从另一个令牌扩展id
,则可能是(0)
而不是0
,或0x10
而不是16
}。此外,还有运行时值。
但是,如果你有一个足够现代的编译器,你可以使用死代码消除:
#define M(id, ...) do { \
if (id) { \
some_code(__VA_ARGS__); \
} \
} while (/* CONSTCOND */ 0)
即使在诸如......的设置中,这也可以解决问题。
void
foo(int bar, int baz) {
M(bar, baz);
}
...而且,由于id
在所有其他情况下应该是常量,if
将被优化掉(即使是GCC 3.4.6,没有任何优化标志也会这样做,对于零和nōn-zero值,我刚检查过; LLVM也会这样做,许多商业Unix供应商编译器可能会这样做; PCC可能不会,但不太可能遇到你。)
所以,这不是一个纯粹的cpp解决方案,而是一个可能在野外工作的解决方案,它不会调用Undefined Behavior或类似的......有趣......或者
/* CONSTCOND */
用于lint,整个do { … } while (0)
块通常用于包含控件构造的宏周围,因此可以普遍使用它们。
some_code
不是代码而是算术表达式在这种情况下,相同的解决方案变得更短:
#define M(id, ...) ((id) ? some_code(__VA_ARGS__) : void)
如果void
为零,则替换最后的id
,替换您希望返回的任何内容。 (我不相信这个问题需要保证这一点,因为写some_code(__VA_ARGS__)
几乎看起来像是一个接受争论的函数,但有些评论人坚持这一点。)