Bendi新闻
>
【老万】我在谷歌弄啥咧(十六):造泵记

【老万】我在谷歌弄啥咧(十六):造泵记

昨天我提到从谷歌学到的最重要技能之一是从第一性原理(first principles)出发思考。

结果这个名词把很多朋友搞懵了。这个要怪我没说清楚,今天就再掰扯掰扯。

所谓第一性原理思维,就是从最基本的原理出发去分析,抓住问题的本质,也就是本质的问题,不接受任何预设的结论。

说人话,就是自己从头推公式,不抄作业。

我昨天还举了一个设计谷歌 C++ mocking 框架 Google Mock(又称gMock)的例子,说到 gMock 是一个嵌入在 C++ 中的DSL(特定领域语言)。

好玩的是早期的 gMock 其实包含了两个 DSL:一个在系统的表面(也就是 gMock 的 API),大家都看得见。另一个藏在 gMock 的实现里面,只有 gMock 的维护者需要了解。

为毛要在一个框架塞入多达个 DSL 呢?到底是中二症还是力比多用不完综合症?

要回答这个问题,咱们还得回到昨天的主题上来:大无畏精神第一性原理思维

我是从 2006 年开始搞 gMock 的。那时候,天苍苍,野茫茫,《老万故事会》还没有诞生,《老万故事会》的读者有很多没有出世,汪峰们还在街上在桥下在田野中唱着那无人问津的歌谣。最重要的是:那时候的 C++ 跟今天比是一种截然不同的语言,不能说是捉襟见肘吧,起码也是一穷二白。

这就给攒 gMock 带来了巨大的挑战。毕竟,它需要利用最高级的 C++ 宏和模版特性来克服 C++ 的静态类型和缺乏反射(reflection)的问题。

我们知道,函数的参数个数有多有少:零元函数不需要参数,五毛函数需要五毛钱参数,一元函数需要一个参数,二元函数需要两个参数,依此类推。参数的个数被称为元数(arity)

gMock 的用户需要 mock 不同元数的函数。为了支持这些需求,gMock 要根据函数的元数提供不同的实现:零元函数有一个实现,一元函数又有一个实现,等等等等。

实现这些功能,最好的办法当然是用上可变参数模板(variadic templates)和可变参数宏(variadic macros)。

然鹅问题来了,多年前的 C++ 并没有可变参数模板这样的高级功能,连可变参数宏也不好使。

最高级的食材,只需要最简单的烹饪。但要是没有高级食材,厨师就只好另辟蹊径。比如,将饭蒸熟后,加入一倍的水,再蒸一次,称为双蒸饭,可以暂时撑饱肚子。又比如,用人尿培养小球藻再让人吃下去,可以治饿痨病。

于是,gMock 的实现代码中被迫出现大量重复。我们可以选择负重前行,手动编写这些代码,但这样得到的只能是一个无法维护的系统:

  1. 写这种重复代码完全是亵渎程序员,没有一个有尊严的程序员会想维护 gMock。当然,我们有很多程序员是没有尊严的,所以这一点也不是什么大问题。

  2. 然而,这些不同元数的实现不是简单的拷贝复制,而是略有不同。复制时很容易犯错误啊啊啊。

  3. 这种方法对系统能支持的元数有人为的上限。要是想支持更多参数的函数,就必须重复更多的代码 — 这就不好玩了。

  4. 要是发现 gMock 有一个 bug,必须在多个地方修正。这种苦活既繁琐又容易出错。


聪明人可能已经想到了:干嘛自己重复写代码,写一个代码生成器不就好了吗。写完了,跑一遍,代码就生出来了。要是发现出来的代码有 bug,那就改改生成器,再跑一遍。一遍不行,就改两遍。

咋说呢,这种方法吧,不是不行,但也不是很行:

这个“代码生成器”听起来就很高大上,实际做起来确实不好维护。毕竟,C++ 和 Python 这样的通用编程语言不是专门为这一类任务设计的。如果写这么一个生成器,要改 gMock 的实现就得费老鼻子劲了。

这种半吊子方案怎么能行呢?一定还有更好的招!

我想,既然用通用语言写这个生成器不顺手,那就来个 DSL 吧。

于是有了 Pump(泵),一个用于编写可变元(variadic)C++ 代码的迷你 DSL。

Pump 这个名字是一个递归缩写:它代表 Pump is Useful for Meta Programming(Pump 对元编程有用)。它也可以代表 Pretty Useful for Meta Programming(对元编程嘿有用),或者代表 Practical Utility for Meta Programming(元编程实用工具)。

三个代表,随你心情而定,总有一款适合你。

取这个名字还有一层含义:“pump”是一个通过不断重复完成任务的动作,比如给气球打气也叫 pump。

是滴,程序员喜欢开这种傻乎乎的玩笑,乐此不疲。我就是一个典型的程序员。

由于 Pump 是专门为编写可变元 C++ 代码量身定做的,C++ 程序员学起来很简单。你可以把普通的 C++ 代码跟 Pump 特有的功能混合在一起使用。

例如,Pump 用 $ 字符开始一个 Pump 关键词,用 $$ 开始一个元注释(就是在 Pump 程序中的注释,不会出现在生成的代码中),用 [[ 和 ]] 将代码分块处理。

我们来看一个具体的 Pump 代码示例:

$var n = 3     $$ 定义一个元变量 n。$range i 0..n  $$ 声明元迭代器 i 的范围。$for i [[               $$ 元循环。// Foo$i 对$i元谓词执行blah操作。$range j 1..itemplate <size_t N $for j [[, typename A$j]]>class Foo$i {$if i == 0 [[  blah a;]] $elif i <= 2 [[  blah b;]] $else [[  blah c;]]};
]]

这段代码经 Pump 翻译后,就成了这样的 C++ 代码:

// Foo0 对0元谓词执行blah操作。template <size_t N>class Foo0 {  blah a;};
// Foo1 对1元谓词执行blah操作。template <size_t N, typename A1>class Foo1 { blah b;};
// Foo2 对2元谓词执行blah操作。template <size_t N, typename A1, typename A2>class Foo2 { blah b;};
// Foo3 对3元谓词执行blah操作。template <size_t N, typename A1, typename A2, typename A3>class Foo3 { blah c;};

熟悉模板语言(templating languages)的朋友可能已经发现了:Pump 就是一个模板语言而已。

有了 Pump,gMock 中的可变元代码编写和维护就简单了。

总之,在面对 gMock 的实现需要大量重复代码的问题时,我发扬大无畏精神,没有因这个问题难解决而放弃。相反,我从第一性原理出发(想清楚 gMock 维护者到底需要什么样的工具),找到了一个办法让维护者自然地表达他们的意图,摒弃了让他们自己搞定代码生成器这种不负责任的想法。

时代的变迁总是会抹去历史的痕迹。随着 C++ 编译器对可变参数宏和可变参数模板的支持慢慢到位,Pump 也渐渐失去了存在的意义,最终迷失在黑夜里被 gMock 放弃了。今天的 gMock 已经完全不用 Pump 了。不过,互联网是有记忆的,你还是能在网上找到我为 Pump 编写的原始文档:https://github.com/google/googletest/blob/release-1.8.0/googletest/docs/PumpManual.md

~~~~


猜你会喜欢《我在谷歌弄啥咧》系列:



~~~~


关注老万故事会公众号:


本公众号不开赞赏不放广告。如果喜欢这篇文章,点个在看,转发给朋友就是对老万的最大支持。谢谢大家🙏

微信扫码关注该文公众号作者

来源:老万故事会

相关新闻

【老万】我在谷歌弄啥咧(十七):以德服人【老万】公司(十四):一箭双雕【老万】我的科大(五):俺怎样奇葩地写论文【老万】老码农潜伏谷歌十七年总结出成功秘籍,前两条竟是...?【小枇杷电台】童年记忆(双语):不一样的香港【老万】我在 NB 公司审查代码【惨剧】谷歌中国工程师枪杀伴侣再自杀未成:二人是清华同学【美国联邦医保系列】医疗补助(Medicaid/Medical assistance)遗失通知:及时行动,重获医疗保障【演化生物学】4. 查尔斯·达尔文(二):自然选择谷歌揭秘:为什么他们长成键盘侠?|【经纬低调出品】【特惠房】東京绝佳房源,3分钟步行至上野车站,仅(?500)万円【EducationUSA活动】赴美留学两部曲:大学申请与校园生活,7月17日&18日(周三 & 周四),18:30-20:00【EducationUSA活动】线上讲座:选择大于努力?找到适合自己的学校与专业,4月2日(星期二)晚上18:30-20:00【EducationUSA在线讲座】探索STEAM领域的多元专业设置,2月1日(星期四), 晚 上18:30-20:00【广发资产研究】越南:战略机遇的沃土——“债务周期大局观”系列(九)【北京美国中心活动】科普讲座:鹰飞之城的空中精灵,5月28日(周二)晚7点【忽然一周】泡泡玛特一季度营收涨四成;美泰、孩之宝发布一季度财报;奥飞娱乐获得外观设计专利授权:“陀螺玩具(摩动SW)”【广州美国中心活动】模拟初选:美国如何选择其候选人,1月20日,星期六,下午2:30-4:00(2:00 开始入场)[旅游] 【非洲,但又不那么非洲】迟到10年的毛里求斯之行(第二篇:毛求啊毛求)【北京美国中心活动】中文科普讲座:鹰飞之城的空中精灵,5月28日(周二)晚7点【广发资产研究】日本固收基金:黄金十载——“债务周期鉴资管”系列(二)唯公生物:自主研发三类淋巴细胞亚群检测试剂(流式细胞仪法)全新上市! 【动脉严选新品鉴第61期】【今晚8点开抢】记忆里的经典国货限量5折!!!(文末有礼)【广发资产研究】一张图看懂《固收基金:黄金十载——“债务周期鉴资管”系列(二)》
logo
联系我们隐私协议©2024 bendi.news
Bendi新闻
Bendi.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Bendi.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。