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

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

职场

昨天我提到从谷歌学到的最重要技能之一是从第一性原理(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

~~~~


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



~~~~


关注老万故事会公众号:


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

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

戳这里提交新闻线索和高质量文章给我们。
相关阅读
皮皮虾:【老友记-雨夜游UCLA】下一个“日经”会出现在哪里?——“债务周期大局观”系列(六)【老万】马一龙降本增笑暴露了哪些问题?歌星之梦「载歌在谷·云集」Q1季报(2024年2、3、4月)突发!一华人工程师被捕!他用谷歌电脑偷了谷歌机密,还存到了谷歌账号???【老万】我的科大(五):俺怎样奇葩地写论文【老何读史杂记】犹太毒枭改变近代中国历史走向中国“第一私人豪宅”:造价27亿,只住2户人家?至今没有人进去过,网友:太逆天了......【老万】我在谷歌弄啥咧(十七):以德服人美股基本面 - 2024_03_07 * 晨报 * 日元兑美元涨1% 薪资数据强于预期促使市场押注日本央行3月加息。美股中概股渡十娘|吴正:生命三部曲之东上海的前世今生(六)抢沙发闹出的大笑话我和老伴(外一篇)【老万】公司(十四):一箭双雕就在这个周日! “载歌在谷” 歌手赛·初赛即将开赛!药你知道|《医疗器械经营质量管理规范》系列解读(六)AGI大辩论!杨植麟:无需定义,李大海:零边际成本,王小川:造医生?张鹏:是信念!【老万】老码农潜伏谷歌十七年总结出成功秘籍,前两条竟是...?2024 EducationUSA 赴美留学,学生签证讲座系列,4月27日(星期六)10:00-12:00罗援泪荐:英魂不泯——败仗中的真英雄(六)新初一二三事(十六)渡十娘|我在谷歌弄啥咧(十七):以德服人欢迎报名,载歌在谷「云集」2024夏日音乐会期待您的加入!写点掉粉的:造“神”需要谨慎7057 血壮山河之枣宜会战 宜昌溃战 13超级主播“董先生”被控性侵!粉丝数超3200万,公司最新回应:造谣,已报警!89岁才成名追求直线之美的艺术家埃雷拉:造物主的存在没有起点与终点【老万】塔维斯多克的邂逅【达特茅斯ED录取专访】我在军事分界线搞和平雷军:造车很苦,但是成功一定很酷!戴康:一张图看懂《下一个“日经”会出现在哪里?——“债务周期大局观”系列(六)》戴康:下一个“日经”会出现在哪里?——“债务周期大局观”系列(六)【老万】手把手把手机出国攻略教给你刚刚,谷歌华人工程师被捕,他用谷歌电脑偷谷歌机密存到谷歌账号...
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。