T230925 Generative Metaprogramming with Macro Preprocessor (Part 3)
宏编程的基本原理都已暗含于解决前两节问题的过程当中,本节开始,依此继续展开。
经由前面的努力,我们已掌握条件逻辑在宏编程的表示,代码生成往往涉及循环,所以今天来讲如何实现一个 FOR_EACH
。
FOR_EACH
用来迭代数据集合,依次取出数据,然后调用函数处理。因此,参数很容易确定,一个回调函数加上可变参数。代码表示:
#define FOR_EACH(call, ...)
至于展开参数,可以利用重载技术加上递归思想,因为重载技术在前文已经有相关组件,所以实现起来非常简单。
#define _FOR_EACH_1(call, x) call(x)
#define _FOR_EACH_2(call, x, ...) call(x) _FOR_EACH_1(call, __VA_ARGS__)
#define _FOR_EACH_3(call, x, ...) call(x) _FOR_EACH_2(call, __VA_ARGS__)
#define _FOR_EACH_4(call, x, ...) call(x) _FOR_EACH_3(call, __VA_ARGS__)
#define _FOR_EACH_5(call, x, ...) call(x) _FOR_EACH_4(call, __VA_ARGS__)
#define FOR_EACH(call, ...) \
OVERLOAD_HELPER(_FOR_EACH_, COUNT_VARARGS(__VA_ARGS__))(call, __VA_ARGS__)
通过层层递归,便可以把大问题拆分成一个个小问题。如果数据集合大小为 5,那么就从 5 开始,处理一个再调用相同的逻辑进行处理。宏不支持真正的重载,其实是通过多个名称不同的宏函数来模拟重载效果,递归同样如此,但是整体的思想是不变的。
现在便可以通过它来生成代码,例如:
void foo(int x) {
printf("x: %d\n", x);
}
#define Foo(x) foo(x);
FOR_EACH(Foo, 1, 2, 3)
/**
* Output:
* x: 1
* x: 2
* x: 3
*/
可以看到,依旧是借助了一个中间层 Foo
来完成调用,因为重载实现中的 call(x)
后面并没有 ;
,以这种中间件来延迟代码生成。为什么可以延迟生成呢?传递之时名称为 Foo
,而非 Foo(arg)
,故只是一个普通的名称,直到将 call(x)
替换为 Foo(x)
时,才真正开始代码生成。
那么对于 0 个参数呢?这里并不是问题,因为我们并不会实际使用该参数,它也不会影响结果,只需如此处理即可:
#define _FOR_EACH_0(call, ...)
进一步,让代码强制展开,以支持 MSVC。最终实现为:
#define _FOR_EACH_0(call, ...)
#define _FOR_EACH_1(call, x) call(x)
#define _FOR_EACH_2(call, x, ...) call(x) _FOR_EACH_1(call, __VA_ARGS__)
#define _FOR_EACH_3(call, x, ...) call(x) _FOR_EACH_2(call, __VA_ARGS__)
#define _FOR_EACH_4(call, x, ...) call(x) _FOR_EACH_3(call, __VA_ARGS__)
#define _FOR_EACH_5(call, x, ...) call(x) _FOR_EACH_4(call, __VA_ARGS__)
#define FOR_EACH(call, ...) \
EXPAND( OVERLOAD_HELPER(_FOR_EACH_, COUNT_VARARGS(__VA_ARGS__))(call, __VA_ARGS__) )
这个工具很有用,可以批量生成许多代码。
比如批量生成类:
#define DECLARE_CLASS(cls) class cls{};
FOR_EACH(DECLARE_CLASS, Bar, Duck)
Duck duck;
批量生成变量:
#define DECLARE_VARIABLES(var) int variable_ ## var;
FOR_EACH(DECLARE_VARIABLES, 1, 2, 3, 4, 5)
variable_1 = 1;
variable_2 = 2;
variable_3 = 3;
variable_4 = 4;
variable_5 = 5;
更多内容,请见下节分享。