老万故事会

【老万】码工的故事(二):老王不照面

上一期码工的故事我们讲了老万如何用删除无用代码大法修正了程序不能编译的大 bug,其时方便面尚温。

这一期的故事也跟面有关,不过此面非彼面。这次是“去年此日此门中,人面桃花相映红”的面,不是“一碗阳春面,亲人两行泪”的面。后面一个面在繁体字中写成“麺”,是麦子做的,和人面意思大为不同。西雅图有家连锁餐馆“面贴麺”,用简体字写出来是“面贴面”,容易让人联想到大宝剑,是打擦边球的典范。

那天老万给老康(我司在东欧的高工合同工)支了一招,让他去掉代码中一行不必要的 #include <jemalloc.h>,解决了 folly 和 jemalloc 两大系统库头文件之间的冲突。

没想到过几天老康又回来了:老万,你的法子好是好,就是有时候使不上啊!

嗯?还有这样的事,说来听听。

老康的罗马尼亚英语一板一眼地从 Google Meet 那端传来:

删码大法的前提是程序里确实用不到这一段代码,但是我们有一个源文件,叫它 foo.cpp 吧,里面既调用了 folly,又调用了 jemalloc 提供的函数 malloc_stats_print(),所以这次必须包含双方的头文件,删无可删。然而它们有冲突,包含在一起会导致 sanitizer 模式编译无法通过。这可如何是好哇?

听到这里,我略加思索。这一次还来不及泡面,方法就有了。

我跟老康说:你下过象棋吗?

老康说:象棋?我会啊。这可是我们欧洲人民的传统项目啊,就跟你们中国人的乒乓球一样。我们这嘎达的象棋特级大师都是一嘟噜一嘟噜的。早些年,我们罗马尼亚还跟着苏联老大哥混的时候,一个大列巴就能换一个象棋特级大师给你通一天下水道。

我将信将疑。这老康有时候嘴上没个把门的,满嘴跑火车,而且是计划经济时代年久失修的蒸汽机车,时不时抛个锚出个轨什么的,根本不靠谱。

我说:你说的是国际象棋,我今天跟你说的是来自神秘东方的中国象棋。你听说过吗?

老康说那我还真不知道,原来中国也有象棋。

我说您老还真是孤陋寡闻了。我天朝上邦除了闪电五连鞭,象棋也有自己的玩法。在中国象棋里面,双方的老王是不能照面的。也就是说。如果它们出现在同一条纵线上,中间必须有其它棋子阻挡,否则就是犯规。谁要是走出了这种格局,必须当场认输,自行了断。

老康想了想说:你是说 folly 和 jemalloc 就像两个老王,不能在同一个源文件里出现?

对。我对着 Google Meet 里老康的头像赞许地点了点头,就是这个道理。

Image

老康又问:你是说我们必须当场认输,自行了断?

我掩面太息:那倒不必,国家留着我们还有用。你看,两个王不能照面,我们只需要用别的棋子放在中间,不就能把它们隔离开,不再照面了吗?

老康说:我还是整不明白。你能不能讲得清楚一点?

我说那好吧,5分钟后看我的 PR(代码修改提议)。用代码可以讲清楚的事,绝对不用比喻。

~~~~

5分钟后,老康的信箱收到了我发过去的 PR,是这样的:

康斯坦丁贤弟台鉴:
今日悉知贤弟发现我司 foo.cpp 源文件如斯
// foo.cpp
#include <folly/hash/Hash.h>#include <jemalloc.h>
... return folly::hash_128_to_64(upper, lower);
...  malloc_stats_print(WriteCallback, data, "");
然 folly 之于 jemalloc,如干柴之于烈火,一龙之于小扎,绝无两全之理。此间难处不比先前,folly 我所欲也,jemalloc 亦我所欲也,何以得兼?
愚兄今有一计,贤弟使之可安天下。只需引入新库 malloc_utils,其头文件 malloc_utils.h 如:
// malloc_utils.h
#pragma once
void PrintMallocStats();
其实现文件 malloc_utils.cpp 状若:
// malloc_utils.cpp
#include "malloc_utils.h"#include <jemalloc.h>
static void WriteCallback(...) { ... }
void PrintMallocStats() { void* data = ...;  malloc_stats_print(WriteCallback, data, "");}
如此,可改写 foo.cpp 为
// foo.cpp
#include <folly/hash/Hash.h>#include "malloc_utils.h"...
return folly::hash_128_to_64(upper, lower);
...
PrintMallocStats();
试观之:foo.cpp 与 malloc_utils.h 不复包含 jemalloc.h,而 malloc_utils.cpp 亦不包含 folly.h,两王为 malloc_utils 一库所隔,动静如参商,人生不相见,断无冲突之可能。不亦乐乎!
此计尚有额外之一好处:原始 jemalloc 库之 malloc_stats_print()函数为面向过程之低级 C 语言接口,难用易错。新库将其封装于一高级函数之中,易用难错,可谓一石二鸟。

总结一下:

如果一个源文件 foo 需要包含两个互相冲突的头文件 a.h 和 b.h,而我们又不便修改 a.h 和 b.h,只需将 foo 分拆为两个文件 foo 和 bar,其中 foo 只依赖于 a.h,bar 只依赖于 b.h,就可以避免冲突了。

老康看罢 PR,两个星期前刚复位的下巴再度脱臼。他把下巴扶回去:哦,我明白了。山外有山,人上有人。我们欧洲的国际象棋,只会搞八皇后这些腐朽没落的封资修,像王不照面这样对编程技术有重大影响的革命性思维半点都没有,在中国的象棋面前啥也不是。还是中国人的老祖宗厉害,以后我再也不敢言必称卡斯帕罗夫、卡尔波夫、帕奇诺夫、人尽可夫了。

这正是:山重水复疑无路,王不照面又一春。莫愁代码不编译,新库上线定乾坤。

~~~~~~~~~~

猜你会喜欢:

~~~~~~~~~~

关注老万故事会公众号:

本公众号不开赞赏不放广告。如果喜欢这篇文章,欢迎点赞、在看、转发。谢谢大家🙏