如何使用CPP预处理器编写递归for循环“重复”宏来生成C代码?

我想强制预处理器为我做一些自动代码生成。 我不需要太多:只是一个包含另一个for循环的简单for循环。[1]

我已经阅读了所有关于宏观扩展的内容,并且在蓝色涂料出现时不再傻笑。 在美好的一天,我甚至可以解释为什么需要多层宏来生成带有标记粘贴的函数名称。 我实际上已经让for-loop工作了。 但是当把循环放在一个循环中时,我会减少随机地使用DEFER,EVAL和OBSTRUCT并且希望最好。

我不会被理智的召唤吓倒。 我确实想用标准的C预处理器来做这件事。 我保证无论结果如何,我,我的雇主和我的inheritance人都不会因技术弊端而起诉你。 我保证如果没有适当的安全眼镜,我不会允许任何人维护代码,甚至不能查看代码。 如果你愿意,假装我只是在考虑理论上的兴趣。 或者我唯一的另一种选择是使用M4:因为如果CPP中的递归宏是变态的,那么M4肯定是整只鸡。

我发现的最好的参考资料是一个有9年历史的Usenet线程: http : //comp.std.c.narkive.com/5WbJfCof/double-cpp-expansion

它开始偏离主题,在语气方面有些小而且好斗,而且是我的头脑。 但我认为我寻求的答案就在那里。

接下来最好的是一个名为Cloak的CPP滥用标题的文档: https : //github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms

它需要一种不同的迭代方法,或许可以满足我的需求。 但它也是一个很好的概述。

这里有一些简短的代码,以显示我被卡住的地方。

repeat.h:

#define REPEAT(macro, times, start_n, next_func, next_arg, macro_args...) \ _REPEAT_ ## times(macro, start_n, next_func, next_arg, ## macro_args) #define REPEAT_ADD_ONE(macro, times, start_n, macro_args... ) \ REPEAT(macro, times, start_n, _REPEAT_ADD_ONE, 0, ## macro_args) #define _REPEAT_ADD_ONE(n, ignore...) _REPEAT_ADD_ONE_ ## n #define _REPEAT_0(args...) /* empty */ #define _REPEAT_1(macro, n, func, i, args...) macro(n, ## args) #define _REPEAT_2(m, n, f, i, a...) m(n, ## a); _REPEAT_1(m, f(n, i), f, i, ## a) #define _REPEAT_3(m, n, f, i, a...) m(n, ## a); _REPEAT_2(m, f(n, i), f, i, ## a) #define _REPEAT_4(m, n, f, i, a...) m(n, ## a); _REPEAT_3(m, f(n, i), f, i, ## a) #define _REPEAT_5(m, n, f, i, a...) m(n, ## a); _REPEAT_4(m, f(n, i), f, i, ## a) #define _REPEAT_6(m, n, f, i, a...) m(n, ## a); _REPEAT_5(m, f(n, i), f, i, ## a) #define _REPEAT_7(m, n, f, i, a...) m(n, ## a); _REPEAT_6(m, f(n, i), f, i, ## a) #define _REPEAT_8(m, n, f, i, a...) m(n, ## a); _REPEAT_7(m, f(n, i), f, i, ## a) #define _REPEAT_9(m, n, f, i, a...) m(n, ## a); _REPEAT_8(m, f(n, i), f, i, ## a) #define _REPEAT_10(m, n, f, i, a...) m(n, ## a); _REPEAT_9(m, f(n, i), f, i, ## a) #define _REPEAT_ADD_ONE_0 1 #define _REPEAT_ADD_ONE_1 2 #define _REPEAT_ADD_ONE_2 3 #define _REPEAT_ADD_ONE_3 4 #define _REPEAT_ADD_ONE_4 5 #define _REPEAT_ADD_ONE_5 6 #define _REPEAT_ADD_ONE_6 7 #define _REPEAT_ADD_ONE_7 8 #define _REPEAT_ADD_ONE_8 9 #define _REPEAT_ADD_ONE_9 10 #define _REPEAT_ADD_ONE_10 11 #define _REPEAT_ADD_0(x) x #define _REPEAT_ADD_1(x) _REPEAT_ADD_ONE(x) #define _REPEAT_ADD_2(x) _REPEAT_ADD_1(_REPEAT_ADD_ONE(x)) #define _REPEAT_ADD_3(x) _REPEAT_ADD_2(_REPEAT_ADD_ONE(x)) #define _REPEAT_ADD_4(x) _REPEAT_ADD_3(_REPEAT_ADD_ONE(x)) #define _REPEAT_ADD_5(x) _REPEAT_ADD_4(_REPEAT_ADD_ONE(x)) #define _REPEAT_ADD_6(x) _REPEAT_ADD_5(_REPEAT_ADD_ONE(x)) #define _REPEAT_ADD_7(x) _REPEAT_ADD_6(_REPEAT_ADD_ONE(x)) #define _REPEAT_ADD_8(x) _REPEAT_ADD_7(_REPEAT_ADD_ONE(x)) #define _REPEAT_ADD_9(x) _REPEAT_ADD_8(_REPEAT_ADD_ONE(x)) #define _REPEAT_ADD_10(x) _REPEAT_ADD_9(_REPEAT_ADD_ONE(x)) 

sample.c文件:

 #include "repeat.h" #define INNER_MACRO(inner, outer) if (inner == outer) printf("Match\n") #define INNER_BLOCK { if (inner == outer) printf("Match\n"); } #define OUTER_MACRO_INNER_MACRO(outer) REPEAT_ADD_ONE(INNER_MACRO, 3, 0, outer) #define OUTER_BLOCK_INNER_MACRO { REPEAT_ADD_ONE(INNER_MACRO, 3, 0, outer); } #define OUTER_MACRO_INNER_BLOCK(outer) REPEAT_ADD_ONE(INNER_BLOCK, 3, 0, outer) #define OUTER_BLOCK_INNER_BLOCK { REPEAT_ADD_ONE(INNER_BLOCK, 3, 0, outer); } void outer_macro_inner_macro() { REPEAT_ADD_ONE(OUTER_MACRO_INNER_MACRO, 2, 1); } void outer_macro_inner_block() { REPEAT_ADD_ONE(OUTER_MACRO_INNER_BLOCK, 2, 1); } void outer_block_inner_macro() { REPEAT_ADD_ONE(OUTER_BLOCK_INNER_MACRO, 2, 1); } void outer_block_inner_block() { REPEAT_ADD_ONE(OUTER_BLOCK_INNER_BLOCK, 2, 1); } 

sample.c我展示了四种接近我想要的变化。 但没有一个在那里。 这是我用“cpp sample.c> out.c; astyle out.c;”得到的输出。

 void outer_macro_inner_macro() { REPEAT_ADD_ONE(INNER_MACRO, 3, 0, 1); REPEAT_ADD_ONE(INNER_MACRO, 3, 0, 2); } void outer_macro_inner_block() { REPEAT_ADD_ONE({ if (inner == outer) printf("Match\n"); }, 3, 0, 1); REPEAT_ADD_ONE({ if (inner == outer) printf("Match\n"); }, 3, 0, 2); } void outer_block_inner_macro() { { if (0 == outer) printf("Match\n"); if (1 == outer) printf("Match\n"); if (2 == outer) printf("Match\n"); }(1); { if (0 == outer) printf("Match\n"); if (1 == outer) printf("Match\n"); if (2 == outer) printf("Match\n"); }(2); } void outer_block_inner_block() { { { if (inner == outer) printf("Match\n"); }(0, outer); { if (inner == outer) printf("Match\n"); }(1, outer); { if (inner == outer) printf("Match\n"); }(2, outer); }(1); { { if (inner == outer) printf("Match\n"); }(0, outer); { if (inner == outer) printf("Match\n"); }(1, outer); { if (inner == outer) printf("Match\n"); }(2, outer); }(2); } 

这是我想得到的输出:

 void desired_results() { { if (0 == 1) printf("Match\n"); if (1 == 1) printf("Match\n"); if (2 == 1) printf("Match\n"); }; { if (0 == 2) printf("Match\n"); if (1 == 2) printf("Match\n"); if (2 == 2) printf("Match\n"); }; } 

基本上,如果我使用块作为外部循环体,我可以使事情起作用,但如果我使用类似函数的宏则不行。 但我需要使用带参数的宏,以便循环体可以将循环计数器用作常量而不是变量。

“宏” – “宏”方式的问题是不扩展对REPEAT_ADD_ONE()的内部递归调用。 答案似乎是推迟内循环的扩展,直到创建外循环,然后强制另一个扩展内循环的传递。 但出于某种原因,我的“随机猴子”方法还没有产生解决方案……

[1]低调说明。

Vesa Karvonen的“Order”库/语言绝对可以帮到您。 它在C预处理器中实现了无限制的递归和循环,并且作为一个非常酷的奖励,它使用了“适当的”编程语言的简洁语法(澄清:这不是一个替代的预处理器,它只是做了很多令牌) – 保持关键字简短。它仍然是纯粹的CPP)。

它使用了一种截然不同的技术,将元程序转换为CPS,然后将它们传递给具有可能数万亿步的单循环结构,并以严格线性的方式执行元程序。 因此,循环和递归函数可以根据您的需要进行深度嵌套,因为它们没有需要交互并相互绘制蓝色的单独驱动程序。

是的,有人使用CPP宏实现了一个完整的虚拟机和解释器。 这太吓人了。

(编辑:如果Rosetta Code已停止为您工作,请尝试存档版本 。)

在这里的答案的帮助下(以及研究P99 , Chaos , Order和Cloak ),我认为我有一个相当简单和紧凑的概念validation(1)。 由于我只想要“重复”function而不是一个完整的解释器,所以我采用了与其他解决方案不同的方法。 我没有创建通用的“if”,“while”或“when”宏,而是直接使用了一系列“递减”宏,这些宏扩展到所需的宏,再加上对宏的调用n-1。

 #ifndef _REPEAT_H #define _REPEAT_H // Usage: REPEAT_ADD_ONE(macro, times, start_n, macro_args... ) // Recursion allowed if inner macros use REPEAT_ADD_ONE_INNER(). // This demo header only allows 3 layers of recursion and max n=10. // Sample code at bottom. #define REPEAT_ADD_ONE(macro, times, start_n, macro_args... ) \ _REPEAT_EXPAND_3(REPEAT_ADD_ONE_INNER(macro, times, start_n, ## macro_args)) #define REPEAT_ADD_ONE_INNER(macro, times, start_n, macro_args... ) \ _REPEAT_ ## times(macro, start_n, _REPEAT_ADD_ONE, ## macro_args) #define _REPEAT_0(args...) /* empty */ #define _REPEAT_1(macro, n, func, args...) _REPEAT_DEFER(macro)(n, ## args) #define _REPEAT_2(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_1(m, f(n), f, ## a) #define _REPEAT_3(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_2(m, f(n), f, ## a) #define _REPEAT_4(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_3(m, f(n), f, ## a) #define _REPEAT_5(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_4(m, f(n), f, ## a) #define _REPEAT_6(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_5(m, f(n), f, ## a) #define _REPEAT_7(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_6(m, f(n), f, ## a) #define _REPEAT_8(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_7(m, f(n), f, ## a) #define _REPEAT_9(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_8(m, f(n), f, ## a) #define _REPEAT_10(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_9(m, f(n), f, ## a) // ... #define _REPEAT_ADD_ONE(n, ignore...) _REPEAT_ADD_ONE_ ## n #define _REPEAT_ADD_ONE_0 1 #define _REPEAT_ADD_ONE_1 2 #define _REPEAT_ADD_ONE_2 3 #define _REPEAT_ADD_ONE_3 4 #define _REPEAT_ADD_ONE_4 5 #define _REPEAT_ADD_ONE_5 6 #define _REPEAT_ADD_ONE_6 7 #define _REPEAT_ADD_ONE_7 8 #define _REPEAT_ADD_ONE_8 9 #define _REPEAT_ADD_ONE_9 10 #define _REPEAT_ADD_ONE_10 11 // ... #define _REPEAT_EMPTY() #define _REPEAT_DEFER(token) token _REPEAT_EMPTY() #define _REPEAT_EXPAND_3(args...) _REPEAT_EXPAND(_REPEAT_EXPAND(_REPEAT_EXPAND(args))) #define _REPEAT_EXPAND(args...) args // ... #endif // _REPEAT_H #ifdef SAMPLE_CODE // to generate code: cpp -DSAMPLE_CODE sample.c // or easier to read: cpp -DSAMPLE_CODE sample.c > out.c; astyle out.c; less out.c // to compile and run: gcc -Wall -O3 -DSAMPLE_CODE sample.c -o sample int printf(const char *format, ...); #define BODY(i) printf("%d\n", i); void simple(void) { REPEAT_ADD_ONE(BODY, 5, 1); } #define INNER(k, j, i) \ printf("(%d, %d, %d)\n", i, j, k); \ if (i == j && j == k) printf("Match!\n") #define MIDDLE(j, i) REPEAT_ADD_ONE_INNER(INNER, 2, 2, j, i) #define OUTER(i) REPEAT_ADD_ONE_INNER(MIDDLE, 3, 0, i) void recursive(void) { REPEAT_ADD_ONE(OUTER, 2, 1); } int main() { simple(); recursive(); return 0; } #endif // SAMPLE_CODE 

我仍然很难理解很多细微之处,但正如其他人指出的那样,一般规则是没有宏可以扩展自己。 解决这个问题的方法是创建一个宏,它可以扩展到它自己调用的点,然后在这个结果周围放置一个包装器来完成扩展。

我最终使用的(常见)技巧是利用函数类型宏仅在括号后面立即扩展的事实。 可以使用“延迟”宏,在被调用的宏名称及其括号之间放置一个“空”标记,然后将其“展开”为另一个宏的参数。

由于参数的扩展发生在与初始扩展不同的上下文中,因此初始宏将再次扩展。 在我的解决方案中,每个级别的潜在递归都需要一个级别的扩展。 如果使用代码来理解它,那么减少扩展数量以检查中间结果会很有用。

感谢您的帮助!

(1)正确,当应用于递归预处理器宏时,“相当简单”的标准非常松散。 但它非常紧凑。

P99可能会为您提供所需的信息。 它有几种类型的宏迭代器,简单的像P99_UNROLLP99_SER等,以及通用的一个P99_FOR

我不确定我在那里关注你的所有宏。 这里的答案(现在也在这里 )解释了如何创建一个通用的REPEAT宏,如下所示:

 #define REPEAT(count, macro, ...) \ WHEN(count) \ ( \ OBSTRUCT(REPEAT_INDIRECT) () \ ( \ DEC(count), macro, __VA_ARGS__ \ ) \ OBSTRUCT(macro) \ ( \ DEC(count), __VA_ARGS__ \ ) \ ) #define REPEAT_INDIRECT() REPEAT 

这需要计数,宏和用户数据。 由于传入的宏是延迟的,因此可以直接和递归地再次调用REPEAT宏。 所以这是你的OUTERINNER重复宏:

 #define OUTER(i, j) { REPEAT(j, INNER, i) } #define INNER(j, i) if (j == INC(i)) printf("Match\n"); EVAL(REPEAT(2, OUTER, 3)) 

这将输出:

 { if (0 == 1) printf("Match\n"); if (1 == 1) printf("Match\n"); if (2 == 1) printf("Match\n"); } { if (0 == 2) printf("Match\n"); if (1 == 2) printf("Match\n"); if (2 == 2) printf("Match\n"); } 

希望这是有道理的。