能否在C宏中放置预处理条件?

15

有没有一种方法可以编写一个C预处理器宏,根据其接收到的参数而展开为不同的内容?

#define foo() ???

/* 1 */
foo(name)

/* 2 */
foo(_)

期望的结果:

/* 1 */
int name;

/* 2 */
/*ignore*/

是的,我知道宏很邪恶。我问这个问题主要是出于好奇。


14
宏非常棒。 - Theodoros Chatzigiannakis
2
你能解释一下它的目的,或者给一个更好的例子吗?该参数必须在编译时固定,这意味着你同样可以使用两个不同的宏名称。 - lurker
@mbratch:在这种情况下,我使用宏来定义一组变量,但有些变量是可选的,我想避免定义它们。多个宏名称不起作用,因为会导致组合爆炸:对于两个变量名,我需要单独的宏来处理foo(a,b)foo(a,_)foo(_,b)foo(_,_) - hugomg
1
你似乎在尝试实现一种重载形式。也许你应该研究一下可变参数宏。参考这个问题:https://dev59.com/BE_Ta4cB1Zd3GeqPCJYG?rq=1 - Theodoros Chatzigiannakis
问题在于我需要对我传递给宏的值进行重载,而不是对参数数量进行重载。 - hugomg
3个回答

17

补充Gavin Smith的答案,实际上您可以在宏展开中检查条件:

#define FOO_name 1
#define FOO__ 0

#define CONC(a,b) a##_##b

#define IF(c, t, e) CONC(IF, c)(t, e)
#define IF_0(t, e) e
#define IF_1(t, e) t

#define FOO(x) IF(CONC(FOO,x), int x;, )

FOO(name) // -> int name;
FOO(_)    // -> /*nothing*/

如果你想冒险的话,你可以很容易地通过帮助宏扩展来允许逗号、抑制宏展开等等。但是,这需要你事先知道所有所需名称。

9
也许可以尝试一些多阶段宏展开?这是Boost预处理器/控制/if库使用的策略。
#define FOO_NAME 1
#define FOO__ 2

#define CONC(a,b) a##_##b
#define FOO(x) CONC(FOO,x)

我认为无法在C宏扩展内部检查条件。

我能想到的最好办法是使用字符串化运算符#将宏参数转换为字符串文字,然后使用运行时函数进行检查。(但这对于您要输出变量声明的情况不起作用。)

例如,以下代码将打印“011”:

#define FOO(x) (strcmp("NAME", #x) ? 1 : 0)

main()
{
    printf("%d", FOO(NAME));
    printf("%d", FOO(1));
    printf("%d", FOO(2));
}

编译器可能会在编译时优化strcmp比较,因此效率不会比真正的预处理条件差。但是,将FOO变为普通函数会更清晰,而且可能同样高效。

有趣的技巧!但是有没有一种方法,我不需要枚举“1”情况的所有可能性呢?例如,如果它是“_”,那么宏就会产生2,如果是其他任何东西,它就会产生1? - hugomg
我更新了我的答案,提供了一个有限的解决方法,但对于你原来的问题不起作用。 - Gavin Smith
很遗憾,在我的情况下我不能使用运行时测试。我开始认为你提供的第一个解决方案是我能得到的最好的。 - hugomg
@hugomg,你想要的是可能的,但需要使用很多不太优雅的预处理器技巧:https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms我会添加一个答案。 - yyny

3

使用这里描述的技巧,可以在编译时实现所需操作。

您可以使用文档末尾定义的EQUAL宏,并执行以下操作:

#define COMPARE__(x) x
#define OPTION_0(x) int x;
#define OPTION_1(x) /* nothing */
#define foo(x) CAT(OPTION_, EQUAL(_, x))(x)

foo(name1) // int name1;
foo(_)     // /* nothing */
foo(name2) // int name2;

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