Monthly Read Posts in Mar 2017

Generate lambdas for clarity and performance

Generate a class of lambdas with auto-return-type deduction.

唯一比较遗憾的是特性需要 C++ 14 的支持,C++ 11 里估计需要用 std::function<> 来做 workaround


Appending to a File from Multiple Processes

经验问题,写客户端的 logger 轮子时应该会经常需要考虑到这个问题。

文章针对的是 POSIX 系统,不过不同系统在一些具体策略/细节上也有区别。

另,Windows 上这个场景有直接的 API 调用保证。

不过有点比较奇怪,chromium 的 base::logging 只提供了 Windows 下的多进程无锁 appending,POSIX/Mach 下依然用的是线程同步锁和全局锁,可能考虑的原因是实现起来坑比较大?


OAuth 2.0 Protocol

  1. oauth2 authorization
  2. principle-of-oauth2
  3. oauth_2_0

第二篇文章基本可以看作第一篇的翻译,但是很多地方都没有翻译出来,所以推荐主要看第一篇文章,把第二、三篇作为补充材料


Inheritance Is The Base Class of Evil

C9 上的一个 Tech Talk,speaker 是 Adobe 的技术大佬,但是我觉得有标题党之嫌。

Talk 中展现出来的哲学观挺有意思的,比如那句 There is no polymorphic type, just polymorphic use of types


Learning STL Multithreading

虽然标题是 STL Multithreading,但是里面的核心内容基本都是原理性的,通用的。

对于 mutex/condition-variable 这类比较高级的 synchronization primitives 和低级的 atomic variables 均有涉及,并且也给了实践建议。

话说右边那个胖胖的小哥的口音听起来真的是容易懂…

另外有个挺有意思的细节,快到最后的时候胖小哥提到了 asyncfuture,他说希望大家能优先考虑这俩玩意儿,但是说了几句话又欲言又止,最后无奈地说 we will discuss these later。。。估计他想了想,就目前标准给的设施和实现情况,要做到 task-based paralleilism,那坑大的都可以把自己埋进去了。

在 C++ 11 中实现 apply

Why apply matters

假设我们要实现一个和具体业务无关的 HTTP(S) 网络 (Restful)API 请求设施 RequestConnection,我们至少要提供两个实现:

  • 客户代码的请求结束时的回调 callback/handler
  • 针对具体业务的 response parser;因为 HTTP 是文本协议,无论 response data 基于何种格式(例如 JSON),我们都要针对具体的业务逻辑场景进行解析

假设我们希望请求的 callback 足够友好,直接将 client 需要的数据通过函数原型暴露出来,例如

1
void OnCheckUpdateComplete(bool succeeded, int last_build_no);

那么我们的 reponse parser 也要相应的能够返回符合 callback signature 的数据,在 C++ 中,我们可以用 std::tuple 来保存 parsed result:

1
std::tuple<bool, int> ParseCheckUpdateResponse(const std::string& data);

但是很不巧的,RequestConnection 是业务无关的,而业务相关的 callback/response-parser 则是通过依赖注入的方式(例如通过模板参数)进入 RequestConnection;这意味着我们需要一个通用的方法能够在编译期完成如下事情:

1
2
3
// Presume magic function Apply
std::string response_data;
Apply(ResponseHandler, ResponseParser(response_data));

神奇函数 Apply() 能够调用一个函数,并且将一个 std::tuple 里的元素按照顺序转换为对应的实参。

简言之,这就是 C++ 17 要引入的 std::apply 所做的事情。

但是如果我们的 codebase 是 C++ 11的,那又如何呢?

How to implement apply in C++ 11

在 C++ 11 里实现 apply(),我们要做两步:

  1. 实现 index-sequence (C++ 14 开始标准库附带了这个实现)
  2. 实现 apply
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
#include <tuple>
#include <type_traits>
template<size_t... Ints>
struct index_sequence {
using type = index_sequence;
using value_type = size_t;
static std::size_t size()
{
return sizeof...(Ints);
}
};
template<class Sequence1, class Sequence2>
struct merge_and_renumber;
template<size_t... I1, size_t... I2>
struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
: index_sequence<I1..., (sizeof...(I1) + I2)...>
{};
template<size_t N>
struct make_index_sequence
: merge_and_renumber<typename make_index_sequence<N / 2>::type,
typename make_index_sequence<N - N / 2>::type>
{};
template<>
struct make_index_sequence<0> : index_sequence<>
{};
template<>
struct make_index_sequence<1> : index_sequence<0>
{};
template<typename Func, typename Tuple, std::size_t... index>
auto apply_helper(Func&& func, Tuple&& tuple, index_sequence<index...>) ->
decltype(func(std::get<index>(std::forward<Tuple>(tuple))...))
{
return func(std::get<index>(std::forward<Tuple>(tuple))...);
}
template<typename Func, typename Tuple>
auto apply(Func&& func, Tuple&& tuple) ->
decltype(apply_helper(std::forward<Func>(func),
std::forward<Tuple>(tuple),
make_index_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>{}))
{
return apply_helper(std::forward<Func>(func),
std::forward<Tuple>(tuple),
make_index_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>{});
}

以上代码就是我在某直播姬的网络框架里所用的实现;根据实际是用来看,quite awesome

Monthly Read Posts in Feb 2017

RESTful架构风格下的4大常见安全问题

  • Don’t forget to check correlations between resources in request URL
  • Some rare used HTTP headers might be helpful
  • Restrict API request frequency

这篇略水,某些常见的问题反而都没有提到,例如大木老师遇到的这个问题


Safe Bitfields in C++

Main observation: Use union to implement/simulate bitfields.

Each bitfield is implemented by using a single union member with mask and bit-length being set up initially.

非常 stunning 的一个实现,但是很可惜,根据 comments 这个实现仍然是 undefined behavior,because it call member functions on inactive union members.

Monthly Read Posts in Jan 2017

Library order in static linking

An object file both provides (exports) external symbols to other objects and libraries, and expects (imports) symbols from other objects and libraries.

During linking, the linker maintains a symbol table, which keeps two important lists:

  • a list of symbols exported by all the objects and libraries encountered so far
  • a list of undefined symbols that objects and libraries demanded to import and were not found yet.

Case 1: the linker encouters a new object file

linker first checks symbols the object file exports, and:

  1. add them to the list of exported symbols
  2. remove them from undefined symbols, if necessary.

Note that, if any symbol has already been in the exported list, then we get a multiple definition error.

Linker then checks symbols the file imports, and added them to the list of undefined symbols, unless they can be found in the exported symbol list.

we can see that for linking pure object files, link order doesn’t matter.

Case 2: the linker encounters a new library

Linker goes over all the object files in the library, and for each one, it first looks at the exported symbols:

  1. If any of the exported symbols are on the undefined list, then this object file is chosen, being added to the link, and is treated as normal object file as above(its exported symbols and imported symbols are processed as normal object file).
  2. If any of the object files in the library has been included in the link, the library is rescanned again(because a lately added object file may require symbols exported from one early examined-but-skipped object file).

when linking is done, if any symbols remain in the undefined list, the linker will throw an undefined reference error.

Note that after the linker has looked at a library, it won’t look at it again. Even if it exports symbols that may be needed by some later library.

An very important corollary: If object or library AA needs a symbol from library BB, then AA should come before library BB in the command-line invocation of the linker.

Use Flags to Solve Circular Library Dependency

Use flags such as --start-group and --end-group to repeatedly scan libraries, until no new import symbol was found.

But may take significant time to finish linking.


Beginners’ guide to linkers

和前一篇着重分析链接顺序不同,这篇(其实更像是一个短 paper)介绍了整个链接的基础知识,适合扫盲。

但是如果有一定基础的话,这篇反而没有什么让人觉得惊奇的点。


Exploring std::unique_ptr

同样是一篇基础扫盲 posts,适用于检查自己是否对 std::unique_ptr 的必备知识存在缺漏的情况。

如果认真阅读过 Scott Meyers 的 Effective Modern C++,那么 post 里提到的,诸如“为什么使用 std::make_unique() 可以带来异常安全” 这种高级议题就变得显而易见了。

另外 post 对 custom deleter 的叙述的反而不够详细。


HTTP Made Easy

非常赞的 HTTP 协议扫盲文章

这篇 post 从 HTTP 1.0 开始讲起,然后过渡到 HTTP 1.1,所以对于 HTTP 1.1 为什么要求 request header 一定要带 Host header 提供了一个非常直观的解释。

此外 post 还增加了 chunked transfer encoding 部分,印象中 Top-down approach 那本 computer networking 是没有提及这部分内容的。


C++ 11 之美

这篇 post 主要有三个点

  • 编译期的类成员存在性检查 & 利用 SFINAE 做静态的分支选择(我的这篇 post 里的实现就是从这篇 post 改进而来)
  • 利用 std::function<> 实现 lazy-computation
  • 演示了如何将字符串 "hello/test/20" 转换为函数调用 hello("test", 20)

Android 三大图片缓存原理、特性对比

讲道理这篇文章很水….

一开始 mark 这篇文章是因为当时还在做安卓直播姬,转眼9个月过去了….

现在为了借鉴文中几个库的架构设计从 pocket 的回收站里挖出来看了一遍,不过想想也就那么回事了….

编译期判断是否存在某个成员函数

之前在某篇文章描述了一个实际上不是那么好用的检查某个模板类型参数是否存在某个函数的方法。

这次会介绍一个相对有用的,在编译期检查某个类是否存在给定成员函数的做法,并且可以根据检查的结果执行不同的代码。

(简而言之就是如何拙劣的模拟一下 duck typing)

假设我们要检查某个类 class Foo 是否存在成员数 bar(int)

实现辅助设施

1
2
3
4
5
6
7
8
9
10
11
template<typename T, typename... Args>
struct has_member_bar {
template<typename U>
constexpr static auto check(const void*)->
decltype(std::declval<U>().bar(std::declval<Args>()...), std::true_type());
template<typename U>
constexpr static std::false_type check(...);
static constexpr bool value = decltype(check<T>(nullptr))::value;
}

检查的方法是尝试生成函数 T::bar 的返回类型,如果不存在相应函数,则 check() 的重载决议会匹配到第二个函数上。

这里引入 variadic template 的原因是我们希望能够检查时同时确定函数的 signature(注意 signature 是不包含返回类型的)。

使用方法:

1
constexpr bool has_bar = has_member_bar<Foo, int>::value;

如果想把这部分做成通用的设施,那只能引入宏,根据需要生成辅助结构的代码;因为一个辅助结构只能检查一个函数成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define DECLARE_HAS_CLASS_MEMBER(NAME) \
template<typename T, typename... Args> \
struct has_member_##NAME { \
template<typename U> \
constexpr static auto check(const void*)-> \
decltype(std::declval<U>().NAME(std::declval<Args>()...), std::true_type()); \
\
template<typename U> \
constexpr static std::false_type check(...); \
\
static constexpr bool value = decltype(check<T>(nullptr))::value; \
}
#define HAS_CLASS_MEMBER(CLASS, MEMBER, ...) \
has_member_##MEMBER<CLASS, __VA_ARGS__>::value

使用前需要先用 DECLARE_HAS_CLASS_MEMBER(class_name) 来创建一个辅助结构,再利用 HAS_CLASS_MEMBER(Foo, bar, int) 来判断是否存在 Foo::bar(int)

如果希望能利用上面的设施在编译期做到根据函数是否存在执行不同的操作,那么则可以借助 SFINAE 来实现:

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
DECLARE_HAS_CLASS_MEMBER(foo);
DECLARE_HAS_CLASS_MEMBER(bar);
struct Foo {
void foo() {}
};
struct Bar {
void bar() { std::cout << "Bar::bar()\n"; }
void foo(const std::string&, int) { std::cout << "Bar::foo(const std::string&, int)\n"; }
};
template<typename T>
typename std::enable_if<HAS_CLASS_MEMBER(T, foo, const std::string&, int)>::type RunFoo(T& obj)
{
obj.foo("hello world", 1);
}
template<typename T>
typename std::enable_if<!HAS_CLASS_MEMBER(T, foo, const std::string&, int)>::type RunFoo(T& obj)
{
obj.bar();
}
int main()
{
Bar bar;
RunFoo(bar);
return 0;
}

但是如果想将 SFINAE 这部分做成通用的 static-if,还是比较 tricky 的;我一开始利用 lambda + SFINAE 倒是模拟了一个,但是因为 lambda 直接被无条件编译,导致即使在编译期已经确定不用某个分支的 lambda,仍然会编译这个 lambda 的代码,使得 static-if 功能大减。

我猜想使用 generic lambda 应该可以绕过去,但是实现上可能会更加 tricky,并且使用上不会太直观,或者满地坑。

其实 HAS_CLASS_MEMBER 最好的作用是等到 C++ 17 普及了 if-constexpr,这样整个流程都变得清晰了。

另外有一点不得不说,其实很多你认为需要通过编译期判断是否存在某个成员函数才能解决的问题,压根就走不到需要自己实现 SFINAE 那一步,通过一些(可能不是那么优雅)简单的构造,就可以利用 type traits 解决,例如,我在实现 KBase Singleton 的方案。

自动开始安装 Inno Setup 打包的安装程序

有时候我们希望用户执行安装程序后,跳过路径选择等一系列确认,自动开始安装,已尽可能减少等待时间。

最明显的例子就是,用户在已经安装程序的情况下,下载了新版的安装包,需要执行更新操作。因为安装路径、设置选项等信息早在用户首次安装时就已经确定,升级安装过程中完全可以跳过。

某科学的直播姬在加入自动更新功能的同时,就需要安装包具备上述能力。

OK,那么如何在 inno setup 中实现自动安装?

Step 1

Inno Setup 提供了 ShouldSkipPage(PageID: Integer) 函数,这个函数会在安装的不同阶段被调用,并且参数是当前安装程序页的 id,函数返回 True,则表示跳过此页。

于是只要重写这个函数,在更新模式下,自动跳过除了 wpInstalling 的所有页。

但是如果只这么做,会发现安装程序还是无法自动开始安装,它会停在 wpReady ,即确认页。

这是 a feature by design,因为 inno setup 的作者觉得不让用户确认就自动开始安装是一个很 evil 的行为;所以即使在 ShouldSkipPage() 里选择跳过确认页,inno setup 也会自动忽略这个要求。

真他喵的体贴啊。

既然只能手动点击安装,那我们就通过发送按钮点击事件来模拟用户点击就好了。

Step 2

在函数 CurPageChanged(CurPageID: Integer) 中检查当前页,如果已经到了确认页,就发送按钮事件。

1
2
3
4
if CurPageID = wpReady then begin
param := 0 or BN_CLICKED shl 16;
PostMessage(WizardForm.NextButton.Handle, CN_COMMAND, param, 0);
end;

这里注意几个坑,按我当时被踩的先后顺序:

  1. 如果你的安装程序定制了 UI,那么极有可能会隐藏原生的 next button 并且提供一个自己美化过的安装按钮;如果真的是这样,那么请通过设置 WizardForm.NextButton.Height := 0 来隐藏他,而不是直接 set invisible。因为后者会导致这个按钮直接不响应他的事件。
  2. 发送消息采用 PostMessage。我一开始用的 SendMessage 并没有成功,具体原因未知…

Rants

Inno Setup 这种试图通过提供一系列设置和受限的脚本的方式来生成安装包的行为,固然可以降低使用门槛,但是也增加了自定义的难度和复杂度。Joel Spolsky 那篇洞察一切的 the law of leaky abstractions 其实已经说明了一切。

其实如果不是接手项目的时候安装包早就已经固定了,我很可能自己就用 C++ 给撸了,如果真这样,也没有这么多七七八八的事情…

至于我为什么会亲自上阵做安装包…还不是因为没有人愿意做,并且之前的 iss 脚本质量实在一般,都快大坑小坑落玉盘了,总得有个人出来 clean the mess/ass,你说对伐(真怀念当年不想搞了可以把脏活直接丢给老大的日子…)。

自动将 non-capturing lambda 转换为函数指针

在开发某科学的直播姬的过程中,经常需要在 obs-studio 处理源之后紧接着做一些事情,例如针对大图片源做自动放缩等。

obs-studio 采用一个专有的 graphics rendering thread 来渲染各种 visualizable sources,并且允许你根据需求,注册各种底层源操作事件的回调函数(obs-studio 自己称之为 signal handler)。

我在 obs signal handler 之上,利用 std::function 构建了一层自己的 signal-handler,将原始的 obs 事件经过合理转换后得到语义更加明确的直播姬专属事件,并且通知上层。

但是前面提到,各种源操作都是在 obs 专属的渲染线程上完成的,那么我们向 obs 注册的 handler 也必然运行在这个专属线程上;这显然不是一个好现象,因为在直播姬程序语义下,这部分操作应属于我们自己定义的 UI 线程,并且用户可见的 UI 元素的操作也必须只能在 UI 线程中完成。

所以我们需要在 signal 转换的同时切换线程,让 std::function 的调用发生在指定的线程。

于是这里遇到这个 post 要解决的问题:我们使用 chromium base lib 来完成各种线程操作,但是 base::Bind() 不接受 lambda 作为 currying 的载体,因为这套 lib 是在 C++ 11 标准化之前就实现的,因此 chromium team 实现这个轮子的时候并没有考虑 C++ 11 的 lambda expressions。

方案1:使用独立的函数来产生 base::Callback

优点是直观明确而且很传统不容易出错;

缺点是,每个函数平均只有5行不到的代码(毕竟只是通过调用 std::function 做一个转发),而且我们要写大量这样的函数。。。另外由于这些独立的函数都放到了源文件开头的 anonymous namespace 里,造成阅读代码时的上下文割裂。这也是当初引入 lambda 的一个很重要的原因。

方案2:使用 non-capturing lambda,但是自己手动 cast 到函数指针

优点是利用 lambda 来限制了这部分“转发”函数的可见性;

缺点是,不仅会造成上下文割裂,而且手动 cast 非常的 tedious。

思索片刻后我打算自己实现一个 lambda_decay(),来将 non-capturing lambda 自动转换为等价的函数指针。

借助 template 和一个 trick 可以做到如下的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
using lpfn = void(*)(const std::string&);
lpfn Foo()
{
return lambda_decay([](const std::string& s) { std::cout << s << std::endl; });
}
int main()
{
auto ptr = Foo();
ptr("hello world");
return 0;
}

这样就可以直接将 lambda “嵌到” base::Bind() 里,极大提升幸福感。

这里说两个我并不认为是缺点的局限性:

  1. 不能使用在 capturing lambdas 上。这其实是我恰好需要的,因为 capturing lambda 会有各种安全隐患,配合 base::Bind() 一个不小心就容易出坑;另外我的目的本来就是转换得到能被 base::Bind() 使用的等价函数指针,而正常情况下从 non-capturing lambdas cast 得到一个函数指针是良好定义的十分明确的行为。
  2. 不能使用在 generic lambdas 上。generic lambdas 很好很有用,但是它不是银弹,尤其在配合 base::Bind() 使用的情况下。

接下来是实现方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template<typename T>
struct dememberize;
template<typename C, typename R, typename... Args>
struct dememberize<R(C::*)(Args...) const> {
using type = R(*)(Args...);
};
template<typename T>
struct lambda_pointerize_impl {
using type = typename dememberize<decltype(&T::operator())>::type;
};
template<typename T>
using lambda_pointerize = typename lambda_pointerize_impl<T>::type;
template<typename F>
lambda_pointerize<F> lambda_decay(F lambda)
{
return lambda_pointerize<F>(lambda);
}

其实如果 VS 2013 支持 constexpr 的话,其实用起来会更优雅。

另:我发现英文在自创单词上简直太有感觉了。。。。