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



关于c语言中的可变宏,这里有几个问题,包括:

  • 如何创建一个可变宏(可变数量的参数),它解释了基础知识,例如,传递一个可变数量的参数给函数,如printf
  • 是否可以在可变宏中迭代参数?,其中解释了如何迭代地将宏应用于可变宏的每个参数。
  • https://github.com/swansontec/map-macro其中解释了如何对
  • 执行此操作

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

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

Before置于所有扩展之前,将Action应用于每个参数,将Between置于每两个连续应用之间,并将最终放置After的扩展。而且,如果在这样一个宏中参数的数量,应该可以写

// 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函数。一般化的功能可以用于更复杂的应用程序。例如,用于定义类和函数的宏,用于打印跟踪的东西,等等。

我从来都不喜欢递归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))

关于参数计数重载宏的更多例子见这个线程。我使用##GNU扩展来删除__VA_ARGS__之前的逗号,因为我习惯了使用它-我认为__VA_OPT__(,)现在应该是首选,我不确定。

最新更新