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 →

Crypto++ 试用

由于某些需求, 需要研究一下加密解密的相关内容, 于是找到了 crypto++, 常见的加密算法, 这里面差不多都有了, 但是想用的话, 还是需要先补一下密码学相关的基础知识.

对等加密

对等加密 (Reciprocal cipher) 又称为对称密钥加密 (Symmetric-key algorithm), 对称加密, 私钥加密, 共享密钥加密, 是密码学中的一类加密算法. 该类密码的加密算法是它自己本身的逆反函数, 所以其解密算法等同于加密算法, 也就是说, 要还原对等加密的密文, 套用加密同样的算法即可得到明文. 换句话说, 若参数 (或密钥) 合适的话, 两次连续的对等加密运算后会回复原始文字. 在数学上, 这有时称之为对合. 在实际应用中, 体现为加密和解密使用同一个密钥, 或者知道一方密钥能够轻易计算出另一方密钥.

常见的对称加密算法有 DES, 3DES, AES, Blowfish, IDEA, RC4, RC5, RC6.

在对称钥匙密码学中, 加密和解密使用相同的钥匙, 也许对不同的信息使用不同的钥匙, 但都面临钥匙管理的难题. 由于每对通讯方都必须使用异于他组的钥匙, 当网络成员的数量增加时, 钥匙数量成二次方增加. 更尴尬的难题是: 当安全的通道不存在于双方时, 如何建立一个共有的钥匙以利安全的通讯? 如果有通道可以安全地建立钥匙, 何不使用现有的通道. 这个 “鸡生蛋, 蛋生鸡” 的矛盾是长年以来密码学无法在真实世界应用的阻碍.

公开密钥加密

公开密钥加密 (英语: Public-key cryptography, 也称为非对称(密钥)加密), 该思想最早由雷夫·莫寇 (Ralph C. Merkle) 在 1974 年提出, 之后在 1976 年. 狄菲 (Whitfield Diffie) 与赫尔曼 (Martin Hellman) 两位学者以单向函数与单向暗门函数为基础, 为发讯与收讯的两方创建密钥.

非对称密钥, 是指一对加密密钥与解密密钥, 这两个密钥是数学相关, 用某用户密钥加密后所得的信息, 只能用该用户的解密密钥才能解密. 如果知道了其中一个, 并不能计算出另外一个. 因此如果公开了一对密钥中的一个, 并不会危害到另外一个的秘密性质. 称公开的密钥为公钥; 不公开的密钥为私钥.

Read on →

返回值类型推导

模版函数的参数类型可以自动推导, 它可以让我们在调用函数的时候不必写丑陋的<>, 但如果是返回值类型需要自动推导, 似乎就没有那么容易了. 虽然语言本身不支持返回值的类型自动推导, 但我们可以尝试其他的办法来完成这项任务.

目的

在使用函数返回值初始化变量或给变量赋值时模版函数能够自动推导出类型.

例子

在某些情况下, 明确被初始化的变量类型是有用的. 考虑下面的例子, 我们使用随机数来初始化 STL 容器. 但是我们不知道用户会选择哪个具体的容器类型. 它可能是 std::list, std::vector 或者其他的行为像 STL 容器的自定义类型. 最直接的方法如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <list>
#include <vector>
#include <random>

template <class Container>
Container GetRandomN(size_t n) {
  Container c;
  for(size_t i = 0; i < n; ++i) {
    c.insert(c.end(), rand());
  }
  return c;
}

int main () {
  std::list<int> l = GetRandomN<std::list<int> >(10);
  std::vector<long> v = GetRandomN<std::vector<long> >(100);
  return 0;
}
Read on →
C++

面向切面编程(AOP)

上周同事问了个没法回答的问题, “统计, 日志应该放到哪一层, 或者哪个模块中?”

首先, 所有的编程范式都不是万能的, 更不用说反并发、反模块化的面向对象范式了. 我们不能总是以同一种编程范式来思考问题. 类似日志, 权限验证, 事务管理等会横跨多个模块的东西, 我们需要新的思路来解决代码割裂的问题.

面向切面编程

面向切面编程 (AOP), 不是什么新玩意儿, 早在 199x 年开始, 研究人员就对面向对象思想的局限性进行了分析. 研究出了一种新的编程思想, 借助这一思想可以通过减少代码重复模块从而帮助开发人员提高工作效率. 随着研究的逐渐深入, AOP 也逐渐发展成一套完整的程序设计思想, 各种应用 AOP 的技术也应运而生. 比较著名的有 Java 阵营的 AspectJ 和 Spring AOP, C++ 中也有 AspectC++.

AOP 中的概念

  • cross-cutting concerns, (横切关注点)指的是一些具有横越多个模块的行为, 使用传统的软件开发方法不能够达到有效的模块化的一类特殊关注点. 如日志, 权限验证等不属于业务逻辑, 但多个模块都需要插入的东西, 即属于横切关注点.
  • advice, (通知)指的是切面中的具体逻辑. 如打印日志, 验证权限.
  • pointcut, (切入点)指的是切面代码要插入的位置.
  • aspect, (切面)指的是通知以及切入点模块化之后的整体.

上面的内容都属于动态横切, 基本上所作的工作就是方法拦截, 插入指定的代码. 还有一种静态横切, Mixin 它可以可以在不修改原有职责的基础上增加新的职责, 可以模拟多继承, 而不增加耦合性, 不过这就不是本文所要讨论的内容了.

看下面的伪代码来感受一下 AOP:

Read on →
AOP