从 XML 到 Lisp
之前学 elisp 的时候一直感觉思维转不过来很纠结,看了 The Nature of Lisp,原来正如 Code Complete - Laying the Foundation 章节讲的那样,我们认识新事物总是会借鉴思维里面已有的 Metaphor, 我只用过 C, C++, Java,Python, Objective-C, 也就意味着我基本只有这些语言的 metaphor,都跟 Lisp 差异很大,也难怪了。但也正因为如此,才需要不断的克服自身的排斥心理,去接触更多的新事物。
正如书中所云:
A software metaphor is more like a searchlight than a road map. It doesn’t tell you where to find the answer, it tells you how to look for it. A metaphor serves more as a heuristic than it does as an algorithm.
接触的新事物越多,积攒起来的 metaphor 就越多,“本能” 的触类旁通便也会越频繁,很多科学上的新发现都是把不相干的事物触类旁通的引用到自身领域而突然使人茅塞顿开的。
该文章从 XML 着手,对于这样一个函数
Test-driven using Objective-C
对 TDD 不了解的同学可参考 Test-driven development
本文使用的 Objective-C 单元测试框架是 OCUnit ,最新的 Xcode 已经包含。
TDD 的步骤如下:
- 写一个测试某个功能的单元测试用例;
- 运行,测试失败;
- 编码实现功能;
- 运行单元测试,通过修改代码,直到测试成功;
- 重构代码;
- 重构单元测试用例;
- 重复 1。
其中 5、6 是可选步骤,有必要了才会进行,但是必须保证产品代码和单元测试用例不能同时被更改。
STL 源码分析-A.万物起源-3
上一篇提到过,第二级 alloc 在分配大于 128 bytes 的时候,直接使用第一级 alloc,如果小于 128 bytes 才采用内存池的方式进行内存分配。
分配过程是这样的,每次分配一大块内存,存到一个 free list 中,下次 client 若再有相同大小的内存要求,就直接从这个 free list 中划出,内存释放时,则直接回收到对应的 list 中。为了管理的方便,实际分配的大小都被调整为 8 的倍数,所以有 16 个 free lists,分别为 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128 bytes。例如需要 20 bytes,将会被自动调整为 24 bytes。free lists 的结构如下
1 | enum {_ALIGN = 8}; |
为了节省内存使用,使用 union
结构,这个设计很巧妙,每一个元素(内存区域对象)即可以当作下一个元素的指针,例如后面代码中的
result -> _M_free_list_link
,也可以当作该元素的值,例如
*__my_free_list
。整个 free lists
的结构便是一串使用后者的元素构成的节点,每个节点的值是使用前者构成的单向链表的首地址。
首先来看 allocate 的过程
STL 源码分析-A.万物起源-2
在分析了对象的构建和析构之后,我们接着看对象构建所基于的内存空间是如何分配和释放的。这一功能是由
stl_alloc
提供的。之前提到过
allocate,在 SGI STL 中是不使用的,因为其只是对 new 和 delete
进行了简单的封装,并且加上了这段代码
1 | set_new_handler(0); |
这个函数用来指定如果内存分配失败时的 callback 函数,定义如下
1 | typedef void (*new_handler)(); |
使用时参数传入 NULL(0) 则表示卸除
new_handler
,也就意味着如果 ::operator new
分配内存失败,则会抛出 std::bad_alloc
类型的异常。
单元测试与程序的重定向和链接
在做单元测试过程中,经常需要对被测程序的一些函数实现 stub,下面三个文件
1 | // product.c |
product.c 为产品代码提供 lib_pro
函数,user.c
为使用者,可以当作 UT 测试函数,stub.c 提供 lib_pro
函数的另一个定义。
通常,为了使用 stub,需要在测试时将 user.c 产生的 user.o 和 stub.c 产生的 stub.o 链接在一起,这样,每当要给某一个文件加 stub 时,都需要替换链接,有没有什么办法可以自动进行这一工作,如果有 stub,就链接 stub,如果没有,就链接产品代码。
当然有,
Emacs lisp 基础
使用 Emacs 大概也有大半年了,越用越觉得它的强大,始终都有惊喜,每次看到一个功能,心中想:Emacs 可以吗?答案真的往往会是 Yes!
1 | (defun factorial (n) |
上面这种充满括号的奇怪的语言叫 Lisp,它是仅次于 Fortran 的最老的高级程序设计语言,名称来源于 LISt Processing,它有两个方言,分别是 Common Lisp 和 Scheme。 Emacs lisp (elisp) 来源于 Common Lisp。使用 Emacs,如果不能自己打造适合自己的功能,虽然仍然可以享受 Emacs 带来的流畅和便利,但却会与另一种乐趣失之交臂,这篇文章简要叙述 elisp 的基本概念和语法。
Hello World
1 | (message "Hello, World!") |
STL 源码分析-A.万物起源-1
C++中,我们熟悉的对象的创建和释放过程如下:
1 | class Thing |
代码中 new 完成两部分动作,1. 调用 ::operator new
分配对象所占内存空间,2. 调用 Thing 构造函数初始化对象内容。delete
也完成两部分动作,1. 调用析构函数清除对象内容,2. 调用
::operator delete
释放内存空间。
STL 将这两部分动作分离了开来,内存分配由
alloc:allocate()
负责,释放由
alloc:deallocate()
负责,对象构造和析构依次由
::contruct()
和 ::destroy()
负责。
具体实现位于 stl_alloc.h
, stl_contruct.h
,
stl_uninitialized.h
中,stl_alloc.h
负责内存分配,stl_contruct.h
负责对象构建和析构,stl_uninitialized.h
提供一系列全局函数来进行内存填充 (fill) 和拷贝 (copy)。其实 alloc
并不符合标准规范,而应该是 allocator,位于 stl_defalloc.h
中。
从 concept 到 iterator_traits
1 | template <class Iterator, class T> |
上面是 C++ 中一个普通的模板函数,调用的时候直接将特定类型变量当参数传入就行,这段程序运用了 Generic Programming(泛型编程/GP)。
GP 要求函数尽量一般化,假设参数可适用于任何范畴,那么函数
find
到底有多一般化?
输入条件,对于类型 Iterator,首先能够以 operator “*”
来操作提取其指向区域的值,并且可以用 operator “++”
来移至下一个 Iterator 对象…
为了表示输入条件,假定 Iterator 必须是一个 iterator,在这个地方 iterator 没有其他意思,只是我们定义出来的一个概念,用来指定一组描述某个类型的条件。