宏定义的高级用法
研究 Boost.Preprocessor 的时候整理的一套”宏元编程”套路:用纯宏实现在编译期展开出 N 个变量。不涉及任何编译器扩展,纯标准 C 预处理器。
目标
1 | int BOOST_PP_ENUM_PARAMS(20, a); |
坑一:拼接参数必须经过两层宏
1 |
为什么不能直接写 a##b?因为 ## 会阻止参数展开——直接 CAT(BOOST_PP_REPEAT_, 1) 拼出来的是字面量 BOOST_PP_REPEAT_1 而不是它展开后的内容。标准解法就是延迟一层:先拼出 ~, BOOST_PP_REPEAT_1,再扫描一轮时 ~ 被丢弃、参数正常展开。
坑二:循环靠”手写递归表”
预处理器没有循环,只能靠宏递归展开模拟。但宏不能直接递归引用自己,所以每个编号的规则引用比它小一号的规则:
1 |
|
REPEAT_1_3 展开成 REPEAT_1_2 加上 m(2,2,d),层层展开就得到了”调用 m 0 次、1 次、2 次”的序列。想支持多少次展开,就把表写多长。
坑三:if/else 靠”查找表”
1 |
|
条件为 0 还是 1,靠 BOOST_PP_BOOL_数字 这张查找表把任意数字归一化成 0/1,再由 IIF_0/IIF_1 选择分支。COMMA_IF 则用来决定两个变量之间要不要补逗号(最后一个不加)。
展开过程
ENUM_PARAMS(20, a) 最终展开为:
1 | a0, a1, a2, a3, ..., a18, a19 |
这套东西就是 Boost.Preprocessor 的核心原理,C++ 模板元编程出现之前,库作者全靠它生成重复代码。调试方法:gcc -E 看预处理结果,一切尽收眼底。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 寰宇体的世界!
评论
GiscusTwikoo
