前言 本系列分为上中下三个篇章,前四章作为上篇,详细介绍了宏在产生式编程中的原理和应用,本章开始步入中篇,正式进入 C++ 的产生式元编程技术。 宏是 C 时期的产物,功能颇为简陋,亦非图灵完备,即或诸般奇技妙诀加身,限制仍多。且调试不易,有所束缚,以是仅作辅助,用来实现简单的代码生成功能。至 C++ 时期,产生式元编程工具陡增,其中以模板为一切的根基,发枝散叶,尔今正向静态反射前进。 本章涉及不计其数的模板技术,主要集中于模板的核心理论和用法,概念错综复杂,技术层出不穷。难度等级绝对不低,是本系列中非常重要的一章。 纵然某些概念和技术你已知悉,也莫要跳过某个小节,因为本系列的重心不在模板编程,而在产生式编程,论述角度将有所差异。 泛型……元编程 传统过程式编程的思路是将逻辑作为函数,数据作为函数的输入和输出,通过无数个函数来组织完整的逻辑需求,好似搭积木,构建出各种物体。 面向对象编程更进一步,思路是将逻辑和数据合并起来,构成一个个类,类中包含各种数据和函数,外部只能借助公开函数操纵这些数据。这种以对象表示现实事物的方式与人的思维更加接近,简化了抽象化的难度。 泛型编程则欲将逻辑和数据分离,并提供一种抽象化数据的方式,使得不同的数据能够使用同一种逻辑,极大降低了代码的重复性。于是,同一个函数能够传递不同的参数类型,这叫函数模板;同一个类能够传递不同的数据,这叫类模板。 C++ 是一种多范式语言,以模板支持泛型编程,允许编写泛型的函数和数据,功能不依赖于某种具体的数据类型,使用 SFINAE 和 Concepts 约束抽象化的类型。这种能力使其支持 Parametric Polymorphism,在多态的选择上不必局限于传统的 Subtype Polymorphism,可以使用编译期多态替代运行期多态。 C++ 中,元编程指的是发生于编译期的编程,模板为 C++ 带来泛型编程的同时,也带来了元编程能力。由模板实现的元编程,称为模板元编程。而产生式元编程,指的是发生于编译期的对于编程的编程,模板本就支持在编译期生成代码,因此模板也能够实现产生式元编程。 抽象化、具体化……模板 在模板世界中,函数不再是具体的函数,数据类型也不再是具体的数据类型。模板宛如一个模具,借其能够产生各式各样的物体,这些物体的颜色、材料可能不尽相同,但是形状、大小是一致的。也就是说,一类物体,必须存在共同的部分,才能够抽象出一个模具,模具的作用就是重复利用这些共同之处,减少重复性。 编程是工具,本质是解决问题,解决问题考验的就是抽象化和具体化的能力。抽象化应对的是现实世界中的不变,而具体化应对的是现实世界中的变化,能够清楚地认识到变与不变是什么,问题也就迎刃而解了。那先来分清抽象化和具体化的概念。 抽象的意思是,在许多事物中,去除非本质的属性,抽出本质属性。抽象化就是呈现出具体事物共同本质的过程。将复杂的现实,简化成单纯的模型,这种抽象化方式称为模型化,所谓问题建模,就是对问题进行模型化,也即抽象的过程。具体的概念相对简单,就是指看得见摸得着的事物,每个事物都是独一无二的,是以变化尤盛。具体化就是将抽象事物加载清晰的过程,是一种有助于理解陌生事物的方式。数据类型是具体事物,能够清晰简明地呈现出不同数据类型的共同逻辑,就是用模板类型代替具体类型的精妙之处。 模板本身是一种抽象化的工具,以其编写的是抽象逻辑,表示某类类型的共有本质,是不变的部分。其本身是没有用的,实际需要的还是具体逻辑,因此需要有具体化的过程,将抽象逻辑转变为具体逻辑。在 C++ 模板中,这种将抽象逻辑转变成具体逻辑的过程,称为实例化。实例化将在编译期将抽象类型替换成具体类型,根据模板生成一个个具体逻辑。这种实例化机制,便是我们所需要的代码生成能力。 与宏不同,模板具体化后生成的代码,并无法直接在生成的源码中看到,但是可以通过 CppInsight 这类工具察看。 比如: template <typename T> inline constexpr T Integer = 42; int… Continue Reading 《产生式元编程》第五章 忆昔年模板三两事

临时更一篇关于 format 的内容,经验之谈,置为三星。 进入 C++ 标准的库,实践时日往往很久,像 fmtlib、range-v3 这些经典库都已存在十年以上。不受标准牢笼,一个库的发展会快速许多,是以其本身的功能要比加入标准的完善很多。例如 fmtlib,它比 std::format 使用起来更加方便,能直接支持 Formatting Ranges、Formatted Output、Terminal Color 等诸多功能,而这些功能要完全加入标准,可能得等到 C++29 了。 我在某个 C++20 库的开发中就需要使用 format,当时想着 fmtlib 功能更加完善,便没有直接使用 std::format。但后来就遇到了问题,首先是 fmtlib 有诸多版本,版本之间可能存在差异,用户可能并不像我们这般熟悉 C++,编译之时,问题千奇百怪;其次是在用户机器上可能会出现一些莫名其妙的问题,而这些问题放到自己的机器上却不会出现,分析起来将花费大量时间;最后是编译错误信息,fmtlib 开发之时,Concepts 还未进标准,而其采用的定制方式是模板特化,稍微出现一点问题便会弹出满屏的模板错误,而这些错误信息和真实错误毫不相关,加大了定位问题的难度。 总而言之,fmtlib 隐藏的坑不小,换成 std::format 能够避免很多细微的隐患,减少普通用户的抱怨声。 然而,std::format 缺少很多 fmtlib 直接具备的功能,替换也并非那么简单,本篇讲解的就是这些替换的细节。 下面分成三部分关键点进行讨论。 第一,运行期定制。 std::formatter 默认只支持编译期定制,在 fmtlib 中存在 fmt::runtime 可以编写运行期的定制。比如,下面是一个使用 fmtlib 定制 response 的例子:… Continue Reading C++20 std::format 替换 fmtlib 的注意点

宏部分完结 本系列断更良久,去年已更新前三章: 《产生式元编程》 第一章 宏编程计数引原理 《产生式元编程》 第二章 自复用代码生成技 《产生式元编程》 第三章 替换蓝染概念纤悉 宏部分的核心理论和技术,于此三章,悉已更讫。利用这些原理和技巧,可以实现一些简单的代码生成工具,得到初步的产生式元编程能力。 本系列原定十章,宏只是其中的第一个模块,属于上篇。上篇完结后,将产生一个产生式元编程库,再步入中篇。由是,本系列由虚向实,自理论技术诞生项目,复从项目推进续篇,循此迭代。 章章衔进,写来岂是容易?再有诸事相羁,停更半载有余,亦是无奈。近来乘暇推进,初版已卒,遂以续更。 本章乃承上启下之篇,为上篇画句号,为中篇起草稿。 GMP 产生式元编程库 本系列将同步产生一个产生式元编程库,名为 GMP,可在 GitHub 找到 https://github.com/lkimuk/gmp 。该库的侧重点是提供产生式编程工具,可在编译期实现代码生成。 基于前三章的内容,已添加宏模块的基本功能,跨平台,可实现简单的代码生成。其余模块将随着后续章节持续添加。 下面是一些使用例子。 获取可变宏参数数量: #include <gmp/gmp.hpp> int main() { // Output: 0 1 2 3 4 5 6 7 printf("%d %d %d %d %d %d %d %d\n", GMP_SIZE_OF_VAARGS(),… Continue Reading 《产生式元编程》第四章 封装合并框架顿立