牛媒体装逼录

听说鲁豫要来采访稣

3Q

鹿邑:听说稣已经从区块链开发转型为牛媒体开发,能说说牛媒体吗?

稣:牛媒体就是三个 Q。

鹿邑:您是说三个季度就能掌握牛媒体?

稣:不是……是说牛媒体不过就是三个队列。

鹿邑:哦?我不会问您是哪三个!

稣:第一个是网络包队列,第二个是音视频包队列,第三个是音视频帧队列。

转码

鹿邑:那经常听到的“转码”到底是啥?

稣:标准多到蛋疼罢了。你的化妆桌,东西怎么摆你说了算,别人摆法不同,但东西是一样的。你搬家时,把它们装起来,用啥来装,怎么装,扔掉哪些不重要的,也是你说了算,别人装法不同,但关键的几样东西还是一样的。

鹿邑:据说转码很烧钱,你们用的机器都很贵?

稣:没有没有,小米游戏本就够装逼。

光栅化

鹿邑:第一次听到“光栅化”这个词时,表示一脸懵逼,给解释一下呗?

稣:稣拿稣法做类比,您就能理解了!Look!

地头力,图片来自百度,如有侵权请联系本人删除。

鹿邑:这就是光栅化?还是没明白呀!

稣:别急!您看这些笔划……稣大笔一挥,横竖撇捺,点折弯钩,一笔走红尘,两笔惊鬼神。稣的内心只有笔法走势。

鹿邑:牛逼死了!赶紧发朋友圈!

稣:没错!关键就是发票圈!为了用现代化的方式传播稣法,咱们可以扫描打印、刊登到报纸、拍照发票圈,这些手段有个共同点——最后出来都是一些像素点。

鹿邑:还真是这样哦!我以前拿放大镜看过屏幕,都是红绿蓝的像素点。

稣:您的理解能力不错。这个把“笔划”变成一系列点的过程就是“光栅化”。举个例子,您在画板上拉一条直线,计算机只需要知道头尾两个点和颜色就能定义这条直线,而光栅化之后就是 N 个这种颜色的像素点。

游戏

鹿邑:疫情以来游戏股涨势普遍大好,想了解一下我现在学游戏开发来得及吗?

稣:哈呵,游戏其实就是利用游戏引擎,根据玩家们的输入动作渲染声音和画面,很容易理解,不过实际开发起来很多坑,您还是买在港股上市的游戏公司的股票就好。

鹿邑:游戏引擎是什么,渲染又该怎么理解?

稣:游戏引擎是管理图像、声音、人机设备等一套套接口的集合。最重要的是 3D 引擎,简单的说,它是您告诉它“画个圆”,它就帮您“画出一些点,这些点组成一个圆”的接口。单说画面的渲染,可以理解为更多步骤更广义的光栅化,产生显存里的像素点数组,最后扔给显示器。

鹿邑:您又要类比吗?

稣:Look!稣写作的地方都在这些纸上,写得好的,会签名、盖章,拍照拿去发票圈。过程很简单,但包含了几个重要概念。纸张相当于显存;那张拿去拍照发票圈的纸是后台缓冲区(back buffer)的渲染目标(render target);盖章环节相当于是输出合并器阶段(output-merger stage);拍照可以理解为整体光栅化;当然拍照时,摄像头的位置、角度,以及打灯也是有讲究的;最后发票圈就当作输出到显示器吧!

鹿邑:好像不难理解了,您总是把啥都说得很简单,真这么简单吗?

稣:入门难而已!精通,则更难……不过一开始如果把一些专有名词、缩写整明白,可以更快入门。比如 Direct3D 的 API 就存在不少缩写,什么 OM、RS 啊,只看代码不看文档的话很容易懵逼。

鹿邑:数学要很好吧?

稣:数学思维要很好,具体的知识可以现学。几乎每步都有数学的应用,但套路比较有限,可以一通百通。举个最简单的例子:AlphaBlend 算法,常用于把人物、道具合成到背景图里,它其实和盖章是类似的,把章盖在字上,既可以看到字,也可以看到章。其计算公式就是用 alpha 值对背景点、前景点的三原色值做个加减乘运算,理解一下就行,都不用背。由于 YUV 有很多种具体规范,记不住,干脆也甭记,只要明白有些处理 YUV 比 RGB 方便,有些则 RGB 更方便就行。RGB 和 YUV 之间的转化也都有公式,一样只要理解不用记。

鹿邑:您觉得难点在哪里呢?

稣:规范太多!就拿最基础的像素点来说,就存在很多种格式,大的分类就有 RGB 和 YUV 系列,按每个分量(R、G、B、Y、U、V)的排序就可以分出好多种,还有按每个分量几位表示来区分,然后这些位是整数、浮点数或者浮点数规范化的整数,整数是有符号还是无符号都可以造成不同。

鹿邑:停!我已经晕了!

稣:就好像人以群分吧!但可以按照性别、年龄、肤色、籍贯、身高、体重、专业、爱好等各种维度细分,现实世界就有诸多标准,计算机里一样一样的。

鹿邑:明白!人艰不拆,没有容易二字。

稣:该睡了,做梦容易……吓醒。

诗盗·孙子编码规范

《#诗盗#·孙子编码规范》:善编码者,架不重构,洞不三补。码至难者缺人,缺人则贫于招人。财竭则急于裁员。

注解

模仿自春秋时吴国将军孙武的《孙子兵法》:

善用兵者,役不再籍,粮不三载,取用于国,因粮于敌,故军食可足也。
国之贫于师者远输,远输则百姓贫;近师者贵卖,贵卖则百姓财竭,财竭则急于丘役。
力屈、财殚,中原、内虚于家,百姓之费,十去其七;公家之费,破军罢马,甲胄矢弩,戟楯蔽橹,丘牛大车,十去其六。
故智将务食于敌,食敌一钟,当吾二十钟;忌杆一石,当吾二十石。

八哥之神【番外篇9】

听说鲁豫要来采访稣
请戴口罩采访

1. 疫情已经好转很多,终于又可以采访稣!最近在忙什么呢?

忙跳槽。最近两次采访之间,稣一共任职三个公司。

稣现在从事什么行业?

“AIoT 是我的爱,绵绵的金山脚下花正开”难道这歌,稣会唱给你听?

唱的还行,您随意。

“什么样的加班是最呀最摇摆,什么样的养生才是最开怀……”

2. 近期剧情似乎给配角更多笔墨?

是的。这是为了让主角晚点死,是拖剧的惯用套路。

樱国是映射日本吗?

如果您认真阅读,就会发现一个细节:《八哥之神前传【9】》里都是说“樱国”,而《八哥之神前传【10】》中施付说的是“日本”。

是哦,这是不小心写错吗?

不是。这是有区别的。《八哥之神前传【9】》里的故事发生在识界,而《八哥之神前传【10】》的故事则发现在咱们这个世界。

3. 上次您告诉我会出现“李心觎”,但《八哥之神前传【10】》出现的却是“李星觎”,这次是写错了吧?

当然不是。这是剧情需要,后面会解释清楚的,先不剧透。

4. 出现“狐狸精”这种神话色彩的人物,是不是违反您之前说的“无神论”?

这个问题稍微复杂点。这故事发生于 1994 年秋天,圣小开才 12 周岁不到。

他只是个孩子呀!!!

被一系列恐怖的景象吓坏,产生幻觉您可以理解吧!

而且后来开讲给陈因提听时,她并未相信。说明这也可能只是开一脸正经讲的鬼故事而已,作为最了解开的陈因提都如此认为,读者有啥理由拿它当神话看?

原来如此,这可以理解为主角的性格塑造吧!

没错!您想,大部分男人要三四十岁才会遇到狐狸精,而开十岁出头就遇到,而且免疫,这难道不够装逼?

5. 稣,您又赢了!另外,“纯狐连玉”和之前出现的“胡小玉”是同一只吗?

显然不是呀!您看,演员都不是同一个!胡小玉是白色的,长相冷艳,气质是性感魅惑型。纯狐连玉,光看姓,就知道是上古神话风,走的是庄严又有点幽默的大姐路线。

胡小玉只存在于圣小开梦中,是圣小开见过的某类美女的凝神具体。名字很像,主要原因是胡小玉的最初形象是按照纯狐连玉梦见的,后来再也没见过纯狐连玉,形象不断模糊,又不断得到其她美女形象加强就进化出胡小玉。

纯狐连玉是真的狐狸精,而胡小玉更像人。

6. 稣的梦究竟是什么奇妙的世界?能用最贴切的语言描述分享吗?

  • 脑的触感比人体表面最敏感的皮肤还敏感。

  • 爱情里只有鲸神链那部分才是最深刻的。

7. 陈博士的干儿子长生和古思是怎么回事?

孟长生是早期基因编辑婴儿里比较成功的一个,性别染色体来自圣小开,其它基因都来自陈博士,以此为模板优化而成。所以被陈博士收为干儿子,并安排在贾总公司担任一个小领导。

古思是完全基因编辑人造人,只有卵巢是根据陈博士的基因设计,专门用于代孕。这在未来很流行哦!现在大家可能还难以接受。

没关系,等大家有钱再说。

8. 还是有很多读者表示看不懂,您能再提示一下吗?

一部剧能不能看,首先选角很重要,然后靠演员、导演、剧组的努力配合,后期制作、宣传也很重要。

稣只是一个编剧啊!看不懂绝壁不是编剧的问题,您下次采访一下各位演员吧!

八哥之神前传【11】

2042 年

陈博士:长生最近怎么样?

贾力劣:工作尽心尽职无可挑剔,就是好多女员工都打他的主意。

陈博士:难道身份泄露了?

贾力劣:不会!这事严格保密,连他自己都不知道。

陈博士:那就好。

贾力劣:不过您的干儿子年纪也不小,女朋友经常换,坚持不结婚,难免被人说三道四。

陈博士:嗨,这就是基因的力量,思想都差不多。

贾力劣:嗯,他的基因几乎和您一样。

陈博士:他比我还过分,我只是不想生孩子,找个丁克结婚还是可以的。

贾力劣:女朋友换得勤,恐被称渣男呢。

陈博士:他只是个孩子呀!

贾力劣:哦!em……呃!那个,按您基因设计出来的代孕女孩已经送给开哥。

陈博士:很好。这才是正事!管我干儿子的私生活,活腻了?

贾力劣:是是是。她的卵细胞 100% 和您一样。您放一百个心。

陈博士:贾总办事就是靠谱,尤其是保密工作。

贾力劣:懂!相关资料已经销毁。

1994 年

黄金灯已经是著名脑外科专家。他回到虎纠小县城度假的一个夜晚,遇到几个流氓为难大排档卖唱的小妹。

黄金灯:年轻人,文明点!

流氓头:摸个奶,我给钱就是,要你管啊!

黄金灯:小姑娘,你愿意吗?

小姑娘:不!不愿意。

黄金灯:听到没?这位姑娘不卖身。

流氓头:你谁啊?大叔!

黄金灯:我是一名脑外科医生。知道小李飞刀吗?

流氓头:哦?李医生?还飞刀!哈哈哈……

黄金灯亮刀:我这几把叫小灯飞刀,比小李飞刀厉害。

流氓头:大叔,你是不是脑子有问题啊?兄弟们,教训一下他!

黄金灯一出手,飞刀准确插在后面两个流氓鞋头,避开脚趾,将鞋钉在地上,刀拔都拔不动。

流氓发现往前走脚就会被刀切,知道遇到高手,吓得不敢动。

黄金灯:来。我不用飞刀,截拳道对付你。

流氓头跪倒:大叔!小的不识抬举,跟您道歉了。

黄金灯:转过去,跟小姑娘道歉。

小姑娘:大叔怎么称呼?

黄金灯:道释·圣小开。

2003 年

陈因提:死开,你家的牌坊为什么写的是龙田氏?

圣小开:上面写的是:龙田圣心,无尽乾坤。

陈因提:瞎说,上面只有三个字!

圣小开:em?我怎么记得是八个字!

陈因提:又发神经?

圣小开:有可能……晚上安定后告诉你。

1994 年

圣小开出生在道州德国鹰熊岛乾坤村,从小就很好奇村外面是什么。

累积无数次冲动,开终于打算勇敢地往西走,穿过盐田,去看看树木后面是什么!

虽然不远,却感觉走了很久。终于走到树木后面,是一条公路,横穿公路后是一片海沙田,听大人说这田盛产地瓜和花生。

强行穿过海沙田,是小土坡,再翻过去是一些河和另一些盐田,再过去就是海。

开明白,一路向西是无法走到远处的高山。心想:“试试向北,看那片树林后面是不是还是海?”

向右转!走着走着就是一个庙,再过去是灵堂。开想了三秒钟,可怕……还是绕过去吧!

但是开太天真了,虽然绕过灵堂,但它附近有一大堆土坟!于是为了避开它们,居然绕迷路!

平时当开要遇到危险时,都会有路人甲乙冒出来提醒,这次居然没有?

咦?那块石头上面好像有字!走进一看,不禁念出上面的字:“龙田圣心,无尽乾坤”。好酷的感觉!

纯狐连玉:这界碑下面是仙山公。

开转身一看,是个黄色衣装的漂亮的姐姐,便问:仙山公是谁?

纯狐连玉:你干妈的父亲。

圣小开:哦?可是我拜了两位干妈,您说的是哪位?

纯狐连玉:是姑婆祖。

圣小开:哦!原来是族谱第一人。

纯狐连玉:赶紧拜一拜。

圣小开双手合十俯身朝拜:好的。敢问姐姐又是谁?

纯狐连玉:我是仙山公陵墓的守护兽,纯狐连玉。

圣小开:什么?胡连玉?

纯狐连玉:纯狐,连玉。

圣小开:好的,小玉姐姐。

纯狐连玉:死囡仔,按辈分我和你姑婆祖同辈。

圣小开:婆婆好!

纯狐连玉:我是狐狸精。你不害怕?

圣小开:狐狸精?我还是孙悟空转世呢!

纯狐连玉:不怕也好。你跪下,给仙山公磕三个头,我可以实现你一个愿望。

圣小开:这么好康?我要长生不老。

纯狐连玉:死囡仔,活那么久干嘛?三千年就不错啦!

圣小开:成交!我磕。

磕完头,纯狐连玉已经不见。

2003 年

陈因提:狐狸精都出来了!你真能做白日梦!

圣小开:我很严肃好不好!后来我还梦见过它,问它为什么选择我。

陈因提:它怎么说?

圣小开:说我是世间少有的能做很真实的连戏剧梦的人。

陈因提:好像很厉害的样子哦。但是这种能力有什么用?

圣小开:这种能力可以让我从梦中进入全人类,乃至全宇宙所有智慧生命的集体意识中。

陈因提:噗!然后呢?

圣小开:神不会救助任何个体,你懂吧?

陈因提:是啊,要不然神应该来治治你这惊人的幻想能力……

圣小开:不开玩笑,人类社会有很多问题不是在明面可以解决的,或者说这些问题也不需要从明面去解决,而应该通过观测集体意识从而影响它。

陈因提:好吧,你赢了,但是能具体点吗?

圣小开:很多问题的根本都是心灵问题。比如说,残疾人很敏感地发现别人看待他异样的眼光,这可能加强他对自己的嫌弃和遗憾。当你看到别人的问题,一个惊讶、困惑的眼神,对方可能解读成你看到他们差劲的一面,自觉得你会嫌弃他们,于是就烙下一个芥蒂。

陈因提:那你到底能做什么?

圣小开:我可以在睡着的时候进入集体意识去观测大家,让大家的意识明白不管好坏,神都不会遗弃他们。当我醒来世界就会更美好。

陈因提:那你好忙哦,赶紧睡……晚上来观测一下我的意识,看我的意识会不会打你。

圣小开:还好,像我这样的,地球上有 23 人。

CComPtr 和 CComQIPtr

问题

CComPtr 和 CComQIPtr 长得这么像,有啥关系和区别?

分析

  1. 看代码 CComQIPtr 继承自 CComPtr,CComPtr<IUnknown> 没问题,但 CComQIPtr<IUnknown> 报错,应该使用 CComQIPtr<IUnknown, &IID_IUnknown>。

  2. 不同类型 CComPtr<> 不能直接互相构造;CComQIPtr<> 则可以,因为 CComQIPtr 会进行目标类型的 QueryInterface。

1
2
3
4
5
CComPtr<IUnknown> u;
// ...
CComPtr<IDispatch> d(u); // error

CComQIPtr<IDispatch> d(u); // right, will call QueryInterface
  1. 两者构造/赋值时,都会进行 AddRef,如果不想 AddRef,可以使用裸指针(必须十分清楚自己在干嘛!)。
1
2
3
4
5
6
7
CComPtr<IUnknown> u;
// ...
CComPtr<IUnknown> u1(u); // will call AddRef

CComQIPtr<IDispatch> d(u); // will call QueryInterface(call AddRef impliedly)

auto raw = static_cast<IDispatch*>(u.p); // won't call AddRef
  1. 两者赋值时,小部分行为不同。以下模板使得,当等号两边类型不同时,CComPtr 为左值和 CComQIPtr 为左值,表现不同。
1
2
3
4
5
6
7
8
9
10
// CComPtr
template <typename Q>
T* operator=(_Inout_ const CComPtr<Q>& lp) throw()
{
if(!this->IsEqualObject(lp) )
{
AtlComQIPtrAssign2((IUnknown**)&this->p, lp, __uuidof(T));
}
return *this;
}

总结

  • 您可以忘记 CComPtr,只使用 CComQIPtr;

  • 或者,尽量使用 CComPtr,只在必要时使用 CComQIPtr。

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
#include <iostream>

#include <atlbase.h>
#include <atlcom.h>
#include <atlstr.h>

MIDL_INTERFACE("00554d55-0000-0000-C000-000000000041")
IA : public IUnknown {
public:
virtual HRESULT STDMETHODCALLTYPE FuncA() = 0;
};

MIDL_INTERFACE("00554d55-0000-0000-C000-000000000042")
IB : public IA {
public:
virtual HRESULT STDMETHODCALLTYPE FuncB() = 0;
};

class A : public IA {
public:
~A() { std::cout << __FUNCTION__ << "\n"; }

HRESULT STDMETHODCALLTYPE QueryInterface(
/* [in] */ REFIID riid,
/* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) {
if (__uuidof(IA) == riid || __uuidof(IUnknown) == riid) {
*ppvObject = this;
std::cout << __FUNCTION__ << "\n";
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}

ULONG STDMETHODCALLTYPE AddRef(void) {
++ref_;
std::cout << __FUNCTION__ << ": " << ref_ << "\n";
return ref_;
}

ULONG STDMETHODCALLTYPE Release(void) {
--ref_;
std::cout << __FUNCTION__ << ": " << ref_ << "\n";
if (0 == ref_) {
delete this;
}
return ref_;
}

HRESULT STDMETHODCALLTYPE FuncA() {
std::cout << __FUNCTION__ << "\n";
return S_OK;
}

private:
int ref_ = 0;
};

class B : public IB {
public:
~B() { std::cout << __FUNCTION__ << "\n"; }

HRESULT STDMETHODCALLTYPE QueryInterface(
/* [in] */ REFIID riid,
/* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) {
if (__uuidof(IB) == riid || __uuidof(IUnknown) == riid) {
*ppvObject = this;
std::cout << __FUNCTION__ << "\n";
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}

ULONG STDMETHODCALLTYPE AddRef(void) {
++ref_;
std::cout << __FUNCTION__ << ": " << ref_ << "\n";
return ref_;
}

ULONG STDMETHODCALLTYPE Release(void) {
--ref_;
std::cout << __FUNCTION__ << ": " << ref_ << "\n";
if (0 == ref_) {
delete this;
}
return ref_;
}

HRESULT STDMETHODCALLTYPE FuncA() {
std::cout << __FUNCTION__ << "\n";
return S_OK;
}

HRESULT STDMETHODCALLTYPE FuncB() {
std::cout << __FUNCTION__ << "\n";
return S_OK;
}

private:
int ref_ = 0;
};

HRESULT CreateObject(REFIID riid,
_COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) {
if (__uuidof(IA) == riid || __uuidof(IUnknown) == riid) {
auto p = new A;
p->QueryInterface(riid, ppvObject);
return S_OK;
} else if (__uuidof(IB) == riid) {
auto p = new B;
p->QueryInterface(riid, ppvObject);
return S_OK;
}
return E_NOINTERFACE;
}

int main() {
std::cout << "---- Raw pointer:\n";
{
IUnknown* u;
HRESULT hr = CreateObject(__uuidof(IA), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
auto a = static_cast<IA*>(u);
a->FuncA();
a->Release();
}
std::cout << "---- ctor\n";
{
CComPtr<IUnknown> u;
HRESULT hr = CreateObject(__uuidof(IUnknown), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
CComPtr<IUnknown> u2(u); // will call AddRef
CComQIPtr<IA> a(u); // will call QueryInterface(call AddRef impliedly)
CComQIPtr<IB> b(a); // will call QueryInterface(call AddRef impliedly)
a->FuncA();
}
std::cout << "---- CComPtr A=B:\n";
{
CComPtr<IUnknown> u;
HRESULT hr = CreateObject(__uuidof(IB), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
CComPtr<IA> a;
a = u; // will call QueryInterface(call AddRef impliedly)
if (a) {
a->FuncA();
}

CComPtr<IB> b;
b = u;
if (b) {
b->FuncA();
b->FuncB();
}

// template<T, Q>
a = b; // failed
if (a) {
std::cout << "failed!\n";
a->FuncA();
}
}
std::cout << "---- CComQIPtr A=CComPtr<B>:\n";
{
CComPtr<IUnknown> u;
HRESULT hr = CreateObject(__uuidof(IB), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
CComQIPtr<IA> a;
a = u; // will call QueryInterface(call AddRef impliedly)
if (a) {
a->FuncA();
}

CComPtr<IB> b;
b = u; // will call QueryInterface(call AddRef impliedly)
if (b) {
b->FuncA();
b->FuncB();
}

a = b; // will call AddRef
if (a) {
std::cout << "OK! B is-a A\n";
a->FuncA();
}
}
std::cout << "---- CComQIPtr A=CComQIPtr<B>:\n";
{
CComPtr<IUnknown> u;
HRESULT hr = CreateObject(__uuidof(IB), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
CComQIPtr<IA> a;
a = u; // will call QueryInterface(call AddRef impliedly)
if (a) {
a->FuncA();
}

CComQIPtr<IB> b;
b = u; // will call QueryInterface(call AddRef impliedly)
if (b) {
b->FuncA();
b->FuncB();
}

a = b; // will call AddRef
if (a) {
std::cout << "OK! B is-a A\n";
a->FuncA();
}
}
std::cout << "---- CComPtr B=A:\n";
{
CComPtr<IUnknown> u;
HRESULT hr = CreateObject(__uuidof(IUnknown), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
CComPtr<IA> a;
a = u; // will call QueryInterface(call AddRef impliedly)
if (a) {
a->FuncA();
}

CComPtr<IB> b;
b = u; // failed
if (b) {
b->FuncA();
b->FuncB();
}
}
std::cout << "---- CComQIPtr B=A:\n";
{
CComPtr<IUnknown> u;
HRESULT hr = CreateObject(__uuidof(IUnknown), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
CComQIPtr<IA> a;
a = u; // will call QueryInterface(call AddRef impliedly)
if (a) {
a->FuncA();
}

CComQIPtr<IB> b;
b = u; // failed
if (b) {
b->FuncA();
b->FuncB();
}
}
std::cout << "---- CComQIPtr<IB>:\n";
{
CComQIPtr<IB> b;
HRESULT hr = CreateObject(__uuidof(IB), reinterpret_cast<void**>(&b));
std::cout << hr << ", " << b << "\n";
b->FuncA();
b->FuncB();
}
std::cout << "---- CComPtr<IA>:\n";
{
CComPtr<IA> a;
HRESULT hr = CreateObject(__uuidof(IB), reinterpret_cast<void**>(&a));
std::cout << hr << ", " << a << "\n";
a->FuncA();
}
std::cout << "---- CComPtr<IUnknown>:\n";
{
CComPtr<IUnknown> u;
HRESULT hr = CreateObject(__uuidof(IA), reinterpret_cast<void**>(&u));
std::cout << hr << ", " << u << "\n";
auto a = static_cast<IA*>(u.p);
a->FuncA();
}

return 0;
}

用 VS2019 应该尽量链接带有 Spectre 缓解措施的库

问题

愉快地装完 VS2019,编译一个使用 ATL 的工程,结果失败。

LINK : fatal error LNK1104: cannot open file ‘atls.lib’

分析

看 VC++ Directories 里的 Library Directories,有一个 C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\atlmfc\lib\spectre\x64,但这个目录并没有 atls.lib

反而 C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.25.28610\atlmfc\lib\x64 目录下有 atls.lib

解决

安装 VS 时,应该选择“带有 Spectre 缓解措施、适用于最新 v142 生成工具的 C++ ATL (x86 和 x64)”。

八哥之神前传【10】

1998 年,周易和萧竫告别

周易:我准备去南洋研究电子现金。

萧竫:老师,您不是一直专研人工智能吗?为啥突然改变方向?

周易:老师对人工智能的现状太失望了,即使再过十年、二十年也没有一种力量能够将人工智能真正落地。

萧竫:三十年、四十年呢?

周易:老师并没有放弃!必须有人做长远打算,做好未来的铺垫。

萧竫:您是说电子现金……就是人工智能未来的希望?

周易:没错!人工智能最终要依靠“钱”来作为连结,把世界上大量机器连接起来,才可能产生机器意识。而这个“钱”,不能是现在的纸币,应该是电子现金。南洋有些国家需要匿名电子货币溪黑签,有实施的土壤。

萧竫:我可以和您一起去吗?

周易:别。你还年轻,有大好前途。好好学习,找个同龄人。

2003 年,周易和施付同居

一天晚上,周易和施付正要休息,门外来了一个女子按门铃。

施付:聪哥,这么晚了,还有人找你?

周易:奇怪呀!外面还下着雨呢!

周易:萧竫!你这么跑来菲律宾了?

周易对房内的施付说:是我的学生。

萧竫:老师,您成家了?

周易:是啊,你呢?

萧竫:没,还没……国内发生了很严重的疫情,现在很多人失业,我研究的人工智能领域一直没什么突破,考虑和老师一样,改研究电子现金,您能帮帮我吗?

周易:你需要什么?

萧竫:您有没有还没发布的电子现金方面的论文?

周易:这……有是有,不过我才开始构思,打算过几年匿名发表的。

萧竫脸色一变:匿名发表?

周易:老师现在很低调,在菲律宾名字都改了,你是怎么找到这儿的?

萧竫:我可找得辛苦了。

施付:要不要进来?

萧竫:不用,不用。我该走了。再见老师。

施付:她是中国人?

周易:是啊,她是我在中国时的女朋友。

施付:哦,我记得你提过她。怎么找到这里来的?

周易:不对劲。我怀疑她被不明势力控制,可能是来套我的研究成果的,看来我的身份已经不安全。

施付:跟我回日本?

周易:好。

2042 年

古思:这个时间点?莫非周老师就是……

圣小开:嗯!后来世界出现区块链,每一个链是一个机器神经元,无数机器神经元通过跨链技术组成一个机器脑,这就是真·人工智能的雏形,最后在量子计算机里发展成现在的样子。

古思:没想到是这样。更没想到周老师居然还谈过两次恋爱!

圣小开:谁都年轻过!

古思:也是。爷年轻时,也是艳福不浅吧?

圣小开:年轻时……em,很年轻,四五岁时,就有两个女孩子说长大以后要嫁给爷。

古思:哦?这么早的事情,爷还记得住?

圣小开:也不是记得特别清楚,因为后来那两个小女孩,就再也找不到了,直到大学毕业后,在母校莫名其妙地认识一个和那两个小女孩里的妹妹同名同姓的学妹。

1987 年夏天

开被父母送到姥姥家,李家村寄养。村口有一个池塘,一片榕树林,据说那是一棵榕树,四百年才长成一片。

池塘的台阶常有人洗衣服,面对池塘,左手边有一个牛棚。

背向池塘,往村里走,会过一条小沟,然后左边有一棵柚子树。

大树下面好乘凉,夏天小伙伴们都在榕树下玩。慢慢地认识笑李子和一对双胞胎。

笑李子本名李小谢,他说叫他谢小李也行,因为他爸姓李,他妈姓谢,但开习惯叫他笑李子,因为他很爱笑,每天拼命要把脸笑到瘫。

双胞胎姐姐叫李星觎,妹妹叫李冰月,她们都很喜欢开。有一次冰月为了单独和开玩耍,把星觎骗到牛棚里,关起来,星觎从小就很淡定,居然就在里面站着,独自玩了很久才被回来的养牛人解救出来。

后来冰月觉得很对不起姐姐,就把开让给姐姐了。

李冰月:姐姐以后嫁给开当大老婆,我当小老婆。

2042 年

古思:好可爱啊,嘻嘻。

圣小开:不不不,这个故事不可爱,还十分恐怖。

古思:哦?后来怎么了?

圣小开:后来爷长大了,去调查这对双胞胎,李家村的大人们都说没有这么一对和爷同龄的双胞胎,甚至村里已经几十年没有女双胞胎!但笑李子是存在的,他比爷小几个月。

古思:那位叫李冰月的学妹呢?

圣小开:肯定不是,学妹是 1997 年的。据说村里的女双胞胎要追溯到 1955 年,她们在 1959 年 1 月 3 日就死于战火……

古思:这不科学啊!

圣小开:是很不科学,爷长大后还梦见过她们一次。

梦境

李冰月:开。

圣小开:冰月?你都长这么大了?

李冰月:哈哈,你也是呀。

圣小开:星觎呢?

李冰月:被我关在牛棚呢!嘻嘻。

圣小开:什么?!关这么多年,不会饿死了吧!

李冰月:呀!是哦,咱们快去救她。

圣小开脑补一副骷髅,心惊肉跳来到牛棚,开门瞬间惊呆了。

李星觎:妹妹,你怎么长这么大了?

李冰月:姐姐,你怎么保持 4 岁的?

圣小开:这个地方是时间隧道吗?咱们赶紧离开。

李星觎:不行,我好饿,没力气离开。

李冰月:我去给姐姐拿吃的。

李星觎:别把我一个人留在这里。

圣小开:你这么小,我抱你出去就好。

走出牛棚,星觎也变成一个大姑娘。圣小开带她们回一栋三层楼。

圣小开:你们俩是 1959 年 1 月 3 日就死于战火的那对双胞胎吗?

李星觎:不是呀。我们是池塘里的美人鱼,我们不能离开水太久。

后来三层楼都被水注满,她们在楼内快乐地游玩。圣小开无法呼吸,赶紧找出口,所有窗们都堵死,最后找到二楼楼梯的小天窗逃出来。但二楼外面没有水,摔下来吓醒。

2042 年

古思:鬼魂和美人鱼都不可能,她们也许也是被父母临时寄养在李家村的吧,就和爷一样。

圣小开:希望是,如果真是这样,那爷还有希望找到她们。

八哥之神前传【9】

自从过去、现在、未来佛都圆寂后,世界进入七鹰劫,人间爱欲繁华,天堂地狱皆被同化。

1976 年,黄金灯在樱国谈恋爱,顺便读研。他本科是读计算机的,理想是实现脑机合一式的意识永生,但由于时代落后,他慢慢意识到,这个技术在他的时代是无法实现的,于是决定改读脑科医学,打算通过克隆加换脑手术让生命延长。

然而现实总是背离理想,这是一个血腥的世界,满地人体器官,即使在大街上散步,他的眼里也都是血红色一片,随时要小心踩到血肉。

血,一片一片一片,拼出你我的缘分。我的爱因你而专生,你的手摸出我的心疼。

由于场面过度血腥,稣吓醒了!

2042 年,床上

圣小开:“你怎么没睡?”

古思:“才几分钟,还没入睡呢?”

圣小开:“什么?爷都已经做了一个梦,吓醒了!”

古思:“什么梦?给我讲讲?”

圣小开:“虽然是梦,但却是真实故事,只不过主角是黄金灯大师。”

1976 年

东湖有个湖心岛叫樱花岛,是谈恋爱的好地方。

黄金灯心想:“去樱花岛吸吸天地灵气,也许我还有救。”

旅游船荷载 26 人,他认真地数了一遍救生衣,确实有 26 个。习惯性地环扫一番,透视人头,都是妖魔鬼怪,突然视线停留在一个美女脸上,有皮肤的,英气逼人,而且似乎也是中国人。

男女之间的故事都从八哥开始……船出八哥,柴油机罢工,老板说:“大家不用慌,很快就到湖心,我们用竹竿撑过去。”

又过了一会儿,船夫感觉竹竿变沉了,好像插到一个东西,提出水面,是一块骨盆……引起恐慌。

黄金灯专业地站出来说:“我上过人体解剖课,这个一定是人类的骨盆,很可能是个 20 岁左右的年轻男子。”

美女也专业地站出来说:“我是实习警察,最近确实有个 21 岁的失踪男子。”

黄金灯:“凶手或帮凶应该是船夫,知道这里深水区里有肉食性鳗鱼,所以把尸体扔到这喂鱼。”

美女:“怎么防止尸体浮起来呢?”

黄金灯:“绑块石头就行。”

2042 年,床上

古思:“好像是个不吉利的开头!”

圣小开:“结尾其实也不太好。”

1976 年,樱花岛

黄金灯:“你好,警官,我是东湖边上医学院的学生,黄金灯。您好像也是中国人?”

美女:“原来是医学院高材生!我爸爸是樱国人,妈妈是中国人,我的中文名叫施付。”

黄金灯:“施付?em……我还是叫你的樱文名吧!”

施付:“哦,那你就叫我京子吧!”

黄金灯:“京子!”心想:“怎么不是惠子……”

施付:“你今天不用上课吗?”

黄金灯:“我晕血,出来放松一下。”

施付:“学医的,还晕血?”

黄金灯:“是我想得太简单了,还没脱敏吧!”

施付:“你刚才不是很淡定?”

黄金灯:“是哦,好像没那么恶心了!咦!我现在看世界,不是血腥模式了!!”

施付:“嗯嗯,你的专业很有用,要加油!”

黄金灯:“京子小姐,你呢?”

施付:“我来祭拜我爸爸,他葬在这岛上。”

黄金灯:“葬在岛上……原来你是富家小姐!怎么当警察呢?”

施付:“我爸爸死于黑帮暗杀,所以我立志要替他报仇,消灭黑帮。”

黄金灯:“好理想。但是以警察的身份报仇,不会有些不方便吗?”

施付:“是的,要以大局为重,不能公报私仇。”

2042 年,床上

古思:“后来他们恋爱了?”

圣小开:“对。大师遇到京子后,开始对解剖脱敏,并苦练小灯飞刀。”

古思:“小灯飞刀?”

圣小开:“是的。小灯飞刀是一种很厉害的武器,他还发明双刀并进。”

古思:“有什么用?”

1976 年,东湖烟花

星空下,黄金灯搂着京子一起看烟花。

黄金灯:“我已经练成小灯双刀!”

施付:“有什么用?”

黄金灯:“我试过单刀瞄准人体模型的心脏,却可能插在骨头上,致死率降低,而用双刀,都撞到胸骨的几率低很多。”

施付:“但是飞刀又不能比枪快呀!”

黄金灯:“飞刀比枪容易弄到,我可以用它保护你。”

2042 年,床上

古思:“理科男的浪漫?”

圣小开:“哈哈,后来大师真的用飞刀帮京子报仇了。”

古思:“他们后来在一起了吧?”

圣小开:“没有。大师后来回国,京子改行在南洋做投资遇到周老师,很狗血地结婚了。所以大师一直称呼周老师为情敌。”

跟 UMU 一起玩 OpenWRT(入门篇20):WOL

需求

家里有个 PC,关机状态,想在公司远程开机,可是家里没人,怎么办?

条件

  • 机器支持并开启 WOL (Wake On LAN)。

  • 机器通过板载网卡连接路由器(USB 有线网卡不行)。

解决

  1. 路由器需要有公网地址,如果没有,请参考《跟 UMU 一起玩 OpenWRT(入门篇10):穿透内网》做中转,总之需要能 SSH 到路由器上。

  2. 路由器上安装 etherwake 或 wakeonlan。两者差别是:wakeonlan 是个 Perl 脚本,使用 UDP 包,不需要 root 权限。

  • 如果是在 openwrt 直接使用 root 账号,建议用 etherwake。
1
2
3
opkg update
opkg install etherwake
etherwake MAC_ADDRESS_OF_PC
  • 如果是 armbian,建议平时使用非 root,所以推荐 wakeonlan。
1
2
3
sudo apt update
sudo apt install wakeonlan
wakeonlan MAC_ADDRESS_OF_PC

测试

组装 PC 两台、Intel NUC 7i7BNH 一台测试用过。