引言 自 C 以来,宏为代码生成工具。至 C++98 则有模板,泛型编程日益盛行。迄于 C++20,引编译期表达式,添 Concepts,元编程基础支持始渐完善。由此元编程之技稍简。而静态反射乃元编程系统之核心,却迟久未至,产生式元编程遂仍繁复。 所述元编程之书文,指不胜屈,其间也以编译期计算为主,奇技淫巧,小大靡遗。而于产生式元编程,言者寥寥,常见于库中直用。于是有此系列,略述浅见,供同道者读阅之。 产生式元编程,即为编译期代码生成的技术,各类系统,特性不侔,用法与能力亦有所殊。是以本系列跨度稍大,然只涉标准,不论自成之法,预计十章左右,从旧有到未来之特性,俱可囊括。 问题 代码生成以宏为先,虽是旧工具,然方今模板元编程的能力尚有不足,在产生式元编程的核心特性「源码注入」进入标准之前,它仍是一种常用的代码生成工具。 宏编程得从可变宏参数开始谈起,可变模板参数的个数可以通过 sizeof…(args) 获取,宏里面如何操作呢?便是本章的问题。 接着便逐步分析问题,实现需求,其间穿插宏编程之原理。 分析与实现 宏只有替换这一个功能,所谓的复杂代码生成功能,都是基于这一核心理念演绎出来的。故小步慢走,循序渐进,便可降低理解难度。 问题是获取宏参数包的个数,第一步应该规范过程。 过程的输入是一系列对象,对象类型和个数是变化的;过程的输出是一个值,值应该等于输入对象的个数。如果将输入替换为任何类型的其他对象,只要个数没变,结果也应保持不变。 于是通过宏函数表示出过程原型: #define COUNT_VARARGS(…) N 根据宏的唯一功能可知,输出只能是一个值。依此便可否定遍历迭代之类的常规方式,可以考虑多加一层宏,通过由特殊到普遍的思想来不断分析,推出最终结果。 #define GET_VARARGS(a) 1 #define COUNT_VARARGS(…) GET_VARARGS(__VA_ARGS__) 目前这种实现只能支持一个参数的个数识别,可通过假设,在特殊的基础上逐次增加参数。于是得到: #define GET_VARARGS(a, b) 2 #define GET_VARARGS(a) 1 #define COUNT_VARARGS(…) GET_VARARGS(__VA_ARGS__) 若假设成立,通过暴力法已能够将特殊推到普遍,问题遂即解决。但是宏并不支持重载,有没有可能实现呢?通过再封装一层,消除名称重复。得到: #define GET_VARARGS_2(a, b) 2 #define GET_VARARGS_1(a) 1… Continue Reading 《产生式元编程》第一章 宏编程计数引原理

今天说一下额外 () 产生不同意义的情况。 多数情况下,额外的()是不影响语义的,但在以下 5 种情况,有无 () 则意义不同,此时 () 就有了特殊的作用。 禁止 ADL。 在「洞悉 C++ 函数重载决议」提到过,使用额外的 () 可以防止 ADL。 namespace N { struct S { }; void f(S); } void g() { N::S s; f(s); // OK: calls N::f (f)(s); // error: N::f not considered; parentheses // prevent argument-dependent lookup }… Continue Reading T230428 Extra Parentheses, Different Meaning