《effecticve Modern C++》预览

虽然最近工作在用 javascript, 但 C++ 也没放下, Scott Meyers 的新书《Effective modern C++》终于在 O’Reilly 上架了. 第一时间买了一本. 2年前的时候, Scott 就给了一版 Effective C++11 的初步想法, 两年后的今天, 顺带着 C++14 终于正式出炉了, 不过目前还是 early release 版本, 还没有经过 review, 正式版要到年底才会放出.

不得不说, C++ 现在终于有了它该有的样子. 大致浏览了一下, 如果现在才开始关注 C++11/C++14 的话, 非常推荐这本书. 如果看过前两代 《Effective C++》和《More effective C++》的话, 那你会再次找到久违的感觉.

全书共分为 6 章, 包括: 类型推导, 新关键字 auto, C++98 到 C++11/14 的变化, 智能指针, 右值引用转移语义与完美转发, lambda 表达式, 并发 API.

关于类型推导, 由于 C++11/14 引入了新的关键字 auto, decltype, 以及新的概念右值引用, 那么类型推导就不仅仅存在于模板中了, 因此从现在开始掌握类型推导的规则是 C++ 的必备技能了.

C++11/14 和以前相比, 变化非常的大, 在这本书中, 用了近 1/3 的章节来讲述如何从 C++98 过度到 C++11/14. 有些甚至推翻了之前的推荐做法, 比如 Item 13: Prefer const_iterators to iterators 就与作者之前的 《Effective STL》中的 item 26 完全相反. 所以对于每个写 C++ 代码的人来说, 这一部分是必须要更新的知识.

对于智能智能, 相信大家早就不陌生, 作者的《More Effective C++》中也讲到了智能指针. 这一次, 终于成为了标准, 本书中也讲到了各种类型智能指针合适的使用场景, 掌握了这些之后, 相信资源管理的问题, 就能够减少很多了.

转移语义很好的解决了性能问题, 完美转发解决了模板函数重载时爆炸式增长的问题, 而右值引用就是这两个特性的基础. 对于那些写库的人来说, 这些特性真是天大的好事.

关于 lambda 表达式, 有了 lambda 表达式, 标准库中的那些算法终于能变得好用了. 在没有 lambda 之前, 在使用每个标准库算法之前, 还需要一个仿函数, 这是一件多么不爽的事情.

对于并发, 在 C++98 以及之前, 标准连多线程的概念都没有, 我们只能使用平台相关的多线程设施, 甚至一些我们觉得没问题的多线程模型都会在这种情况下出问题. (比如: 这篇C++ and the Perils of Double-Checked Locking) 现在标准中不仅有了 thread, 甚至还有了更高级的 task, future, promise. 当然如果能有 Resumable Functions 就更好了, 不过这个特性可能要等到 C++17 了.

Read on →
C++

Javascript New Keyword: Yield

生成器与迭代器

在以前写代码的时候, 涉及到迭代算法时, 通常整个过程中都需要维护一个状态变量, 而我们想使用迭代算法的中间值得时候, 不得不使用回调函数.

下面是一个斐波那契数列的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function do_callback(num) {
  console.log(num);
}
function fib() {
  var i = 0, j = 1, n = 0;
  while (n < 10) {
    do_callback(i);
    var t = i;
    i = j;
    j += t;
    n++;
  }
}
fib();

上面的代码中使用了回调函数, 将小于 10 的斐波那契数列的元素输出到控制台.

迭代器和生成器提供了一个新的, 更好的途径来做同样的事情. 下面是使用生成器实现的代码:

Read on →

首尔-釜山-青岛8日游

五一假期请了 3 天假, 和几个比较好的朋友一起韩国自由行. (原本预谋好的是台湾自由行, 但是由于小伙伴的证件问题, 只能换了路线 ╮(╯▽╰)╭)

出国当然必备护照, 据说有小伙伴办护照填表格瞎填一通, 还被带到了小黑屋.( ⊙ o ⊙ ). 大家要小心千万不要乱填哈. 关于护照可以参考这里.

韩国签证还是比较容易办理, 如果没时间去领事馆的话淘宝花 300 块钱左右就可以代办了, 不需要本人办理, 不需要面签.

酒店在国内提前订好, 回避广告, 就不说在哪里定的了, 行程安排是 首尔-釜山-首尔 各住 2 晚. 人均 200 CNY/天. 釜山便宜一些, 首尔较贵, 但住的都不差. 如果能稍微提高一点点预算的话, 能住的非常好呢.

特别安排了 首尔-釜山-首尔 的路线, 坐了韩国的 KTX 高铁, 对外国游客韩国有 KR PASS, 可以直接在网站上预定. 定了 3 日票, 3 日内可以韩国火车随便乱坐, 虽然我们只坐了 首尔-釜山 的往返, 但也比直接买票划算不少. 关于 KR PASS 还差点搞出乌龙, KR PASS 有普通票和同行票, 同行票需要一个普通票带着才可以, 一张普通票最多可以带 5 个同行票(大概是 9 折左右的优惠), 差点全都买成同行票, 还好在出发前又重新定好. 人均 520 CNY.

作为马斯洛需求体系最底层的 WiFi 需求, 当然要提前做好工作, 由于不通韩语, 英语又没信心 ╮(╯▽╰)╭, 最终决定在国内定 wifi egg(3G路由), 国内还. 40CYN/天. 如果是韩国取韩国还的话, 还能再便宜一些. 但是作为一个极度缺乏安全感的人来说, 还是选择了多花一点点钱, 保证万无一失. ^_^

虽然做了不少准备工作, 也还是有不少 bug, 同行的小伙伴没有一个人用的是 中国电信, 而韩国只有 CMDA2000, 导致大家电话都无法使用, 不过 7-11 就有卖电话卡, 也不是什么大问题. 但是毕竟出门在外, 避免有了信号大家乱跑, 最终还是一致决定所有人都紧紧围绕在 WiFi egg 周围, 保持队形.(^__^) 不分开.

最后就是机票了, 往返机票大约 2400 CNY/人. 国际机票税比较高. 提前一个月定的机票, 如果再提前一些的话可能还会更便宜.

废话结束.

Read on →

Web 前端开发小测验, Part 1 之 CSS

这是 @devqinNADbb 上发的一个找虐的测试. (原作者有个提示: Warning: might hurt your feelings).

我来挨个找证据, 今天是 part 1, CSS 部分.

1)

1
2
3
ul {
    MaRGin: 10px;
}

Are CSS property names case-sensitive?

CSS 属性名是大小写敏感的吗 ?

: 不敏感. Cascading Style Sheets, level 17.1 Forward-compatible parsing 最后有这么一段话,

All CSS style sheets are case-insensitive, except for parts that are not under the control of CSS. I.e., in CSS1, font family names and URLs can be case-sensitive. Also, the case-sensitivity of the CLASS and ID attributes is under the control of HTML.

因此, 只有不受 CSS 控制的, 如 font family 的名字, url, 以及受 HTML 控制的 IDclass 大小写敏感, 其他受 CSS 控制的内容都是大小写不敏感的.

2) Does setting margin-top and margin-bottom have an affect on an inline element ?

margin-topmargin-bottom 对内联元素是否有效 ?

: 没效果, Cascading Style Sheets, level 14.2 Inline elements 中有如下描述:

If the inline element has margins, borders, padding or text decorations attached, these will have no effect where the element is broken.

CSS2.1 中, 8.3 Margin properties: ‘margin-top’, ‘margin-right’, ‘margin-bottom’, ‘margin-left’, and ‘margin’ 节关于 margin-topmargin-bottom 有如下描述:

These properties have no effect on non-replaced inline elements.

关于 non-replaced 与 replaced element 的定义可以参考 CSS2.1 中的 Replaced element

3) Does setting padding-top and padding-bottom on an inline element add to its dimensions ?

Read on →
css, web

护照, 往来港澳通行证以及大陆居民往来台湾地区通行证办理记录

首先, 吐槽一下, 在自己国家的土地上都不能自由通行, 还需要办理各种证件. (╯‵□′)╯︵┻━┻

以西安为例, 西安市出入境管理中心在科技路二号(西斜七路十字西南角), 工作时间为法定工作日 9:00-17:00. 据说中午有休息时间. 由于本人 14:50 才到, 中午休息时间未知. (如果一次性需要办多个证件, 需要填多张表格, 所以即使中午休息时间到也没有关系, 领表格自行填写, 填写完之后再办手续).

表格的填写一般都有样例, 不过样例可能已经与现在填写的版本不一致, 这个不用担心, 主要填写基本信息, 不知如何填写的可以留空. 办理时, 工作人员会最后确认的. (这次给我办理的是个制服美女. O(∩_∩)O 哈哈~ 跑题了.

护照

护照是一个国家的公民出入本国国境和到国外旅行或居留时, 由本国发给的一种证明该公民国籍和身份的合法证件. 没有护照是没法出国的. 目前对大陆免签的国家非常少啊, 非常少. (╯‵□′)╯︵┻━┻

护照申请表格一式一份 2 张, 需要本人填写的只有一些基本信息, 办理过程需要录入指纹, 申请表格有一栏是需要工作人员确认信息完成后, 当面签字的. 如果需要邮寄的话, 需要在最后填写邮寄地址. 如果旧护照已过期, 那么与首次申请办理护照没有区别, 旧护照可以自己留作纪念.

往来港澳通行证

内地居民因私往来香港或澳门特别行政区旅游, 探亲, 从事商务, 培训, 就业等非公务活动, 向户口所在地的市, 县公安出入境管理部门提出申请, 凭公安出入境管理部门签发的往来港澳通行证及有效签注才能前往.

如果持有大陆护照, 以及入台的各种证件, 和香港转机机票, 则不需要往来港澳通行证即可在港澳最多停留 7 日.

由于西安目前还不是港澳自由行的城市, 因此还不能办理个人 G 签, 只能办理团队 L 签. 如果只想去香港, 那么只能从深圳找当地旅行团协助过关. 至于有效期及签注次数个人自行选择. 本人选择的是澳门 1 次有效签注, 有效期 1 年. 香港 2 次有效签注, 有效期 1年.

往来港澳通行证申请表格一式一份 1 张, 除了基本信息, 需要注意的是申请的签注类型, 首选个人 G 签, 如果所在城市不支持, 那工作人员会自动帮助你更改为团队 L 签. 如果需要邮寄的话, 需要在最后填写邮寄地址.

Read on →

[译]GotW #95 Solution: Thread Safety and Synchronization

原文地址: GotW #95 Solution: Thread Safety and Synchronization

这篇 GotW 是来回答一些关于线程安全与同步的问题的. 我们的讨论几乎适用于所有主流语言

问题

JG 问题

1) 竞态条件(race condition)指的是什么? 它有很严重吗?

2) 什么是正确同步的程序? 你是如何实现的? 请具体说明.

Guru 问题

3) 考虑下面的代码, some_obj 是一个多个线程可见的共享变量.

1
2
3
4
5
// thread 1 (performs no additional synchronization)
code_that_reads_from( some_obj );  // passes some_obj by const &

// thread 2 (performs no additional synchronization)
code_that_modifies( some_obj );    // passes some_obj by non-const &

如果线程 1 与线程 2 能够并行, 那么当 some_ojb 是如下类型时, 代码是否能够正确同步?

  • a) int
  • b) string
  • c) vector<map<int,string>>
  • d) shared_ptr<widget>
  • e) mutex
  • f) condition_variable
  • g) atomic<unsigned>

提示: 虽然有 7 个类型, 但实际上答案只有两种.

4) 外部同步, 意味着使用共享对象的代码需要自己来保证对象的同步. 回答下面有关外部同步的问题:

  • a) 一般的外部同步的职责是什么?
  • b) 什么是”基本的线程安全保障”?
  • c) 哪些内部同步是在共享变量的实现中需要做的?

5) 完全的内部同步类型(线程安全类型), 意味着所有的同步在对象内部完成, 外部不需要再进行同步. 哪些类型是内部同步的, 为什么?

Read on →

Why Make_shared ?

C++11 中引入了智能指针, 同时还有一个模板函数 std::make_shared 可以返回一个指定类型的 std::shared_ptr, 那与 std::shared_ptr 的构造函数相比它能给我们带来什么好处呢 ?

优点

效率更高

shared_ptr 需要维护引用计数的信息,

  • 强引用, 用来记录当前有多少个存活的 shared_ptrs 正持有该对象. 共享的对象会在最后一个强引用离开的时候销毁( 也可能释放).
  • 弱引用, 用来记录当前有多少个正在观察该对象的 weak_ptrs. 当最后一个弱引用离开的时候, 共享的内部信息控制块会被销毁和释放 (共享的对象也会被释放, 如果还没有释放的话).

如果你通过使用原始的 new 表达式分配对象, 然后传递给 shared_ptr (也就是使用 shared_ptr 的构造函数) 的话, shared_ptr 的实现没有办法选择, 而只能单独的分配控制块:

1
2
auto p = new widget();
shared_ptr sp1{ p }, sp2{ sp1 };

如果选择使用 make_shared 的话, 情况就会变成下面这样:

Read on →
C++

当 Windows API 遇上 RAII

什么是 RAII (Resource Acquisition Is Initialization) ?

RAII (Resource Acquisition Is Initialization), 也称为”资源获取就是初始化”, 是 C++ 语言的一种管理资源, 避免泄漏的惯用法. C++ 标准保证任何情况下, 已构造的对象最终会销毁, 即它的析构函数最终会被调用. 简单的说, RAII 的做法是使用一个对象, 在其构造时获取资源, 在对象生命期控制对资源的访问使之始终保持有效, 最后在对象析构的时候释放资源.

RAII 是保证代码异常安全的重要基础设施. RAII 的使用场景有很多, 如: C++11 中的智能指针, scope lock, scope exit 等等. (早在2000年,Andrei Alexandrescu 就在DDJ杂志上发表了一篇文章,提出了这个叫做 ScopeGuard 的设施)

当 Windows API 遇上 RAII

Windows API 大多是 C 语言风格的函数和句柄, 或者是 COM 风格的接口, 这些用起来都不太方便, 需要进行一定的封装. 至于为什么要封装就不用多说了, 如果你想要异常安全, 想要不必在每个分支中都写清理代码的话, 你一定知道利用 RAII 封装的意义.

ATL 中有对 COM 接口的封装, 智能指针 CComPtr, CComQIPtr 解决了一遍遍的手工 Release 以及 QueryInterface. 但对于普通的 C 语言风格的函数和句柄呢? 难道还要一遍遍的 CloseHandle , ReleaseDC, GlobalUnlock 麽? 弱爆了.

借助 ScopeGuard 和 lambda 表达式(⊙_⊙)? 可以是可以, 但是并不是所有的资源获取都会成功, 那么每次都要产生一个具名的 ScopeGuard, 在申请失败的时候调用 Dismiss, 取消清理的动作嘛? 像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
Acquire Resource1
ScopeGuard release1([&] { /* Release Resource1 */ })
if (!Resource1) {
  release1.Dismiss();
  // throw exception or return or...
}

Acquire Resource2
ScopeGuard release2([&] { /* Release Resource2 */ })
if (!Resource2) {
  release2.Dismiss();
  // throw exception or return or...
}
Read on →
C++

异步编程和延续传递风格

什么是延续传递风格(Continuation-passing Style)?

Continuation-passing style(CPS) 是指将控制流 (Control flow) 显式的当做参数传递的编程风格. 函数的返回不在通过 return 语句, 而是将返回值当做参数, 调用控制流. 延续传递风格的函数都会有一个额外的参数k, 显式的表示了continuation (可以理解成控制流的流向, what comes next). 当延续传递风格函数需要返回的时候, 调用k, 并将返回值作为k的参数.

延续传递风格的函数都有一个额外的参数 k, 表示控制流. 函数需要返回, 必须显式的调用 k. 在函数的末尾调用了另外一个函数, 这种调用称为尾调用, tail call. 相应的在尾部递归调用, 称之为尾递归, tail recursion. 延续传递风格的所有函数都是尾调用.

看一个实际的例子, 假设我们有一个函数 show 可以用来打印一些东西, 通常的做法是我们调用一个函数然后存储或者修改它的返回值, 然后把它传给下一个函数,

1
2
3
4
5
6
auto show = [](const auto& v) { std::cout << v << "\n"; };

auto make_one = []() { return 1; };

// prints 1
show(make_one());

在延续传递风格中, 函数需要增加一个参数用来处理函数返回的结果, 它是这个函数处理完之后需要的后续处理,

1
2
3
4
auto one_cont = [](auto&& k) { return k(1); };

// Also prints 1
one_cont(show);

使用延续传递风格最初主要用于编译高级语言时一种中间代码表示, 有了这种中间代码, 编译器的复杂度大大降低. 各种的控制流都能变为 CPS. 有很多办法可以将非 CPS 代码自动转换为 CPS 的代码. 有兴趣可以去研究下 Compiling with Continuations 这本书.

Read on →
FP

C++11 Std::tuple 和它的应用

模板类 std::tuple 是一个固定大小, 存储元素类型不同的集合. 它是 std::pair 的泛化版本.

一个 tuple 可以显示的声明它每个元素的类型, 也可以用 std::make_tuple 模板函数来实现自动类型推导. 可以用 std::get 指定索引来访问 tuple 中的元素. 如下:

1
2
3
4
5
6
std::tuple<string, int> t2("bitdewy", 123);

auto t = std::make_tuple(string("bitdewy"), 10, 1.23);   // t will be of type tuple<string, int, double>
std::string s = std::get<0>(t);
int x = std::get<1>(t);
double d = std::get<2>(t);

当编译期我们需要一个存放不同类型数据的集合, 但又不想定义一个具名的类时 tuple 是非常有用的. 例如 std::functionstd::bind 就使用 tuple 来存放参数(我们都知道 std::bind 从第二个参数开始, 就是函数的参数了, 参数个数是不定的, 类型也是不定的, 这太适合用 tuple 来定义以及存储函数参数列表了). 尤其是 C++11 开始支持变长模板参数了, 这样一来 tuple 就变得更方便了.

std::tie

很多时候我们都希望函数能够返回两个或者更多个值, std::tie 可以帮助我们解决这个问题. std::tie 会构造一个每个元素都是左值引用的 std::tuple. 所以当一个函数返回一个 std::tuple 时, 我们可以使用 std::tie 构造一个 std::tuple 来接收这些返回值. 同时, 如果我们的类的每个元素都支持比较的话, 我们还可以直接使用它来构造一个 std::tuple 来使用 std::tuple 的比较函数. 如下:

Read on →