C 预处理器中宏参数的广义迭代

Generalized iteration over arguments of macro in the C preprocessor

这里有几个关于 C 中可变参数宏的问题。这些包括:

我的问题与迭代技术有关。我对具有这种通用语义的宏感兴趣。

ITERATE(Before, Action, Between, After, Empty, ...)

Before 放在所有扩展之前,将 Action 应用于每个参数,在每两个连续的应用程序之间放置 Between,最后将 [=16 的扩展放在=].而且,如果argument的个数有了这样的宏,应该可以写成

// Loop elements
#define A(x) (x) 
#define Befor (
#define After )
#define Between ||
#define Empty 1

// Define an OR macro
#define OR(...) ITERATE(Before, A, Between, Empty, __VA_ARGS__)

// Use it
OR()      // Expands to 1
OR(a)     // Expands to ((a))
OR(a,b)   // Expands to ((a)||(b))
OR(a,b,c) // Expands to to ((a)||(b)||(c))

目的当然不是写OR函数。通用功能可能适用于更复杂的应用程序。例如,用于定义 类 和函数的宏、用于打印轨迹的内容等

我从来不喜欢 recursive REPEAT() 宏惯用语 - 它会生成可怕的一小时长的读取错误消息,这些错误消息是..递归的,所以你不知道错误在哪里,也很难理解 OBSTRUCT(REPEAT_INDIRECT) () 东西有用。总体而言,根据参数数量重载宏并使用外部工具(shell 脚本或 m4 预处理器)生成 C 源代码更容易,更易于阅读、维护和修复,您还可以扩展宏工具端消除了 C 端递归扩展的负担。考虑到这一点,您的 ITERATE 可以使用现有的预处理器库生成,P99_FORBOOST_FOREACH 浮现在脑海中。

此外,一直输入 shift 很奇怪 - 我更喜欢蛇形。这是一个没有 BeforeAfter 宏的简化示例,并且在参数数量上重载了宏:

#define _in_ITERATE_0(f,b,e)           e()
#define _in_ITERATE_1(f,b,e,_1)        f(_1)
#define _in_ITERATE_2(f,b,e,_1,...)    f(_1)b()_in_ITERATE_1(f,b,e,__VA_ARGS__)
#define _in_ITERATE_3(f,b,e,_1,...)    f(_1)b()_in_ITERATE_2(f,b,e,__VA_ARGS__)
// or you could expand it instead of reusing previous one with same result:
#define _in_ITERATE_4(f,b,e,_1,_2,_3,_4)    f(_1)b()f(_2)b()f(_3)b()f(_4)
// etc.... generate
#define _in_ITERATE_N(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,N,...)  _in_ITERATE_##N
#define ITERATE(func, between, empty, ...)  \
    _in_ITERATE_N(0,##__VA_ARGS__,9,8,7,6,5,4,3,2,1,0)(func, between, empty, ##__VA_ARGS__)


#define _in_OR_OP(x)     (x) 
#define _in_OR_EMPTY()     1
#define _in_OR_BETWEEN()  ||
#define OR(...)   (ITERATE(_in_OR_OP, _in_OR_BETWEEN, _in_OR_EMPTY, ##__VA_ARGS__))

// Use it
OR()      // Expands to (1)
OR(a)     // Expands to ((a))
OR(a,b)   // Expands to ((a)||(b))
OR(a,b,c) // Expands to to ((a)||(b)||(c))

输出:

(1)
((a))
((a)||(b))
((a)||(b)||(c))

有关根据参数数量重载宏的更多示例,请参阅 this thread。我正在使用 ## GNU 扩展来删除 __VA_ARGS__ 之前的逗号,因为我已经习惯了使用它 - 我认为 __VA_OPT__(,) 现在应该是首选,我不确定。