<?xml version="1.0" encoding="gb2312"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>云风的 BLOG</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/" />
    <link rel="self" type="application/atom+xml" href="http://blog.codingnow.com/atom.xml" />
   <id>tag:blog.codingnow.com,2009://1</id>
    <link rel="service.post" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1" title="云风的 BLOG" />
    <updated>2009-07-02T16:40:41Z</updated>
    <subtitle>思绪来的快去的也快，偶尔会在这里停留</subtitle>
    <generator uri="http://www.sixapart.com/movabletype/">Movable Type 3.2b5</generator>
 
<entry>
    <title>关于“群”的那些破事</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/07/popo.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=478" title="关于“群”的那些破事" />
    <id>tag:blog.codingnow.com,2009://1.478</id>
    
    <published>2009-07-02T15:31:55Z</published>
    <updated>2009-07-02T16:40:41Z</updated>
    
    <summary>在 QQ 增加所谓“群” 这个功能之前，我就不用 qq 了。（所以找我要 qq 号的朋友不要再问了） 但是“群”这个讨厌的东西，总是阴魂不散的游荡在我的网络世界里。 今天在 twitter 上看见 Fenng 说 “我真受不了我可爱的同事们了，你们就不能不用&quot;群&quot;啊? 这是 IM 工具最烂的一个设计。除了浪费时间，还能干什么?” ，真是心有戚戚啊。 当然，人和人对事物的看法见解是不一样的。物以类聚、人以群分吧。我还真见过真心喜爱“群”的同学，大体上和 xmpp 同学以及Fenng 随后的发言一致。虽然分享“小笑话、新闻链接、有趣的小图片” 我觉得应该属于 google reader 的事情， popo 作为网易内部交流工具，当初设计群的时候，我是发过言的。情况大致是这样：...</summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="网易" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p>在 QQ 增加所谓“群” 这个功能之前，我就不用 qq 了。（所以找我要 qq 号的朋友不要再问了）</p>

<p>但是“群”这个讨厌的东西，总是阴魂不散的游荡在我的网络世界里。</p>

<p>今天在 twitter 上看见 <a href="https://twitter.com/Fenng/status/2433112885">Fenng 说</a> “我真受不了我可爱的同事们了，你们就不能不用"群"啊? 这是 IM 工具最烂的一个设计。除了浪费时间，还能干什么?” ，真是心有戚戚啊。</p>

<p>当然，人和人对事物的看法见解是不一样的。物以类聚、人以群分吧。我还真见过真心喜爱“群”的同学，大体上和 <a href="https://twitter.com/xmpp/status/2433359020">xmpp 同学</a>以及<a href="https://twitter.com/Fenng/status/2433384674">Fenng 随后的发言一致</a>。虽然分享“小笑话、新闻链接、有趣的小图片” 我觉得应该属于 google reader 的事情，</p>

<p>popo 作为网易内部交流工具，当初设计群的时候，我是发过言的。情况大致是这样：</p>
]]>
        <![CDATA[<p>一开始，dingdang 认为 popo 没有一个专门做产品的人，都是程序员自己在想应该怎样怎样。这样是做不好产品的。姑且不论程序员主导开发好或不好。我当时也是很赞同应该有人专门去考虑 popo 作为一个产品应该怎么制作和发展的。</p>

<p>IM 是个不错的东西，（虽然我个人更喜欢 email）。应该好好做。我那段时间跟 popo 的人聊的很多。这大约是 03/04 年的事情。</p>

<p>我是很反对增加“群”这个东西的。但是据说“用户”很需要。虽然我一直都觉得，<a href="http://it.keso.cn/2007/07/05/dont-listen-to-users/">不要听用户的</a> ，但是，做一个类似但更好的东西是有必要的。</p>

<p>更早一点，大约是 02 年。当时 popo 组的负责人黄（后来去做飞信的那位兄弟）曾经问我，除了游戏和 IM 我们还可以做点什么？我想了一下说，个人意见是：互联网上最需要的还是人和人的沟通。但不局限于 IM 。从那时起我就在想，到底我们需要一种怎样的沟通工具，可以让更多的人发出的有用的信息方便的聚集到自己这里。</p>

<p>所以、在 popo 增加群功能前，我把自己的想法写了几千字，希望可以做一个更好的“群”。不过 popo 组的人最终拒绝了我的提案。因为很难跟用户解释。用户没有见过这样的东西，而“群”已经深入人心，只要做一个一样的，就至少不会错了。</p>

<p>记得当时我们争论的问题还有类似的许多。比如到底是否存在一种最好的信息组织方式。用户的行为能不能引导。比如说，我坚持，搜索是一种比分类更好的方式，只要适当引导，用户会更偏向前者；但是反对的声音是，有人天生就热衷与自己分类，大类分小类的分下去；搜索只会让信息更乱。（所以、popo 差点就在好友管理中加入树状分类结构，最终只是程序和 UI 设计人员觉得麻烦而没能实施）</p>

<p>话说回来、当时我的建议归结起来很简单：就是类似 twitter 的方式。每个人的消息不要有准确的接收者，而是尽可能的广播出去。在消息里加入适当的关键词。（类似 twitter 的 @ 和 # ）。而后，接收者自己选择过滤出自己感兴趣的信息。</p>

<p>作为 IM ，还可以针对特定消息随时展开聊天室讨论。讨论的过程可以全文从 web 上查阅历史。从这点上有点点类似于后来出现的贴吧。就是以某个主题为线索展开，而不是以固定的人群，固定的聊天室。这也是对<a href="https://twitter.com/lichuang/status/2433630604">需要闲聊扯谈的人</a>的一个满足。</p>

<p>时隔五年，现在这些概念相对好解释的多。因为现在我们有了 twitter 有了 facebook google reader …… 而用户，接收了它们。但我还记得当年，花了两个小时，才给一个人解释清楚我想要的是什么。而他在听完之后的第一反应是：高频率的广播消息？用户会受不了的。</p>

<p>后来、群的功能被加上。如同 qq 的 clone 。</p>

<p>如我所料、在群的忠实用户之外，还有一群如我一样的用户，即使被加入了群，也几乎不去查看群的信息。久而久之、许多群已经失去了当初设计他的人希望达到的功能。（当然，作为 clone 产品，我们需要的并不是考虑为用户带来点什么、也不必要去希望达到什么功能，而是考虑如何 clone 的跟本体一样就行了）</p>

<p>又过了几年，终于公司内部发通知，要求员工上班不得使用群。（虽然部分所谓工作群除外，但事实上否定了群的意义）</p>

<hr />

<p>本质上，网易是个不愿意创新的公司，这点几乎写到了骨头里。不过这也不算坏事。这样，公司的发展更为稳重、少犯错误。我也不因此抱怨。即使脑子里总会蹦出些新奇的想法，但不至于冲动的立刻去干，在这样的环境下，会有更多的时间思考。因为、大部分新想法其实是不切实际的。</p>

<p>哦，我说的是“创”新。对于被验证过好的新事物，公司还是跟的很紧的。比如，gmail 刚内部测试放号的时候，我弄了一个玩。立刻惊叹于基于 ajax 的 web mail 可以这样好用。当天晚上就跟丁说，我们的 web email 系统也应该这样做。然后没多久，163 的 email 就更新了。老板推动的事情总是很积极的 :) 。</p>

<hr />

<p>btw, 我们的  popo pidgin 插件已经可以收发群消息了。需要的同事去 sf checkout 代码自己编译使用。</p>
]]>
    </content>
</entry>
<entry>
    <title>玩了一下 ActionScript</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/06/actionscript3_socket.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=477" title="玩了一下 ActionScript" />
    <id>tag:blog.codingnow.com,2009://1.477</id>
    
    <published>2009-06-28T11:34:18Z</published>
    <updated>2009-06-28T12:24:02Z</updated>
    
    <summary>周末。 玩了一下 ActionScript 。因为感觉做一些简单的需要长连接的互联网应用，flash 是一个不错的选择。在大多数情况下，比要求用户安装一个客户端要人性。（当然，和要求用户为浏览器安装一个莫名其妙的 ActiveX 控件相比，让用户自己决定是否下载独立客户端要友好的多） 因为，虽然 Flash 大多数情况下作为一个浏览器插件（在 Windows 下是一个 ActiveX 控件）的形式存在，但其安全性比之许多绿霸之流的流氓软件还是值得信任的。...</summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="技术" />
            <category term="网络与安全" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p>周末。</p>

<p>玩了一下 ActionScript 。因为感觉做一些简单的需要长连接的互联网应用，flash 是一个不错的选择。在大多数情况下，比要求用户安装一个客户端要人性。（当然，和要求用户为浏览器安装一个莫名其妙的 ActiveX 控件相比，让用户自己决定是否下载独立客户端要友好的多）</p>

<p>因为，虽然 Flash 大多数情况下作为一个浏览器插件（在 Windows 下是一个 ActiveX 控件）的形式存在，但其安全性比之许多绿霸之流的流氓软件还是值得信任的。</p>
]]>
        <![CDATA[<p>身边的同事没有人做过 flash 的应用。周末里，也不好意思的打搅那些做 web game 组的同事。所以我只好求助于 google 以及 adobe 的官方网站，花了几个小时时间，稍微摸出点门道来。</p>

<p>我想我是患上了 IDE 恐惧症。一开始搜出的信息是，开发基于 Flash 的网络应用程序，需要在 Adobe 出品的 Flash CS 中工作。我在网上找来几张这个 IDE 的截图就被吓坏了。这是给我这样愚笨的程序员用的吗？离开了命令行、管道这些东西，我写的代码还能被编译运行吗？我不想把生命浪费在拖拽控件、寻找菜单等等的鼠标操作上。</p>

<p>继续搜索，找到两个东西：</p>

<p>一个是 <a href="http://www.mtasc.org/">MTASC</a> ，第三方开源项目，可以用来编译 ActionScript 2 的代码。</p>

<p>另一个是 Adobe 自己 <a href="http://www.adobe.com/products/flex/">Flex SDK</a> ，可以用来编译 ActionScript 3 。</p>

<p>比较了一下 as 2 和 as3 的语法，很明显，as3 更像是设计出来的语言，虽然写起来比 as2 冗长许多。不过我想，as3 更有前途。所以选择了 Flex SDK  里带的 mxmlc 做我的编译器。</p>

<p>btw, 在 Ubuntu 下安装 MTASC 更为方便，用 apt 即可。随手装了一个，感觉编译速度快很多，有空的话，我也多玩一下。</p>

<hr />

<p>我想试一下，如果用 as3 创建一个长连接跟 server 通讯。搜索的结果是，使用 flash.net.Socket 或是 flash.net.XMLSocket 。</p>

<p>两个差不太多，不过 Socket 可以用 raw binary 的协议，和我以前做的东西跟贴切。所以我用 flash.net.Socket 做的实验。</p>

<p>Socket 类采用的是回调机制，注册一些事件回调去相应网络消息即可。<a href="http://livedocs.adobe.com/flex/2/langref/flash/net/Socket.html#includeExamplesSummary">在 Adobe 的官方文档上找到了例子程序</a>，编译通过。（注：如果使用 mxmlc 编译，需要加上编译开关 -use-network）</p>

<p>然后，我用 nc 架了个简单的 server 做测试。</p>

<p>第一次握手，可耻的失败鸟。nc 收到了一个字符串，“policy-file-request” 然后就被 Client 关掉。</p>

<p>从字面上猜想，这个是 Flash 的安全设施之一，保障 flash 程序挂在 web 上，可以让人更安全的使用。比如，你不能做个 web 页面，放个 flash 在上面，骗别人去浏览，然后用这个 flash 程序去访问一些本来不该你可以访问的服务。</p>

<p>举个例子：如果是你发布一个客户端程序，让用户运行，你可以用这个客户端收集用户机器上的资料，并发回 server ，用户是很难知道的（尤其在 Windows 下）。这点，Flash 可以用本地文件访问的限制控制。</p>

<p>如果你的客户端程序去连接用户本地网络（原本是用防火墙阻止外部连接），用户也很可能在不知情的情况下，泄露一些不应该泄露的信息。Flash 作为一个公众安装量极大的 web 控件，必须考虑这类安全问题。</p>

<p>Flash 的安全解决方案就是，在建立任何网络连接时，先询问 server 自己是否有权限建立连接。也就是自我约束。</p>

<p>为了让我的程序可以正常工作，<a href="http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7c60.html">我在 Adobe 的网站上找到了详细说明</a>。</p>

<p>接下来，随手写了几十行 C 程序提供这个 policy-file-request 服务。我的第一个 as3 程序就可以正确的和 server 通讯了。</p>

<p>剩下来的工作，和别的语言/开发环境下做互联网应用基本没有区别：接收包，分析包、处理包。</p>

<p>Flash 在处理 UI 方面还是很强大的，所以搭建一个诸如聊天室，或者是让一个 sprit 在虚拟场景里跑，并同步各个 client 的位置信息都是很容易的事情。</p>

<p>用 Flash 做所谓“无端网游" 大抵如此。只不过把用 C/C++ 写的东西搬到 as3 里做罢了。至于 Server 那边，该怎么做还是怎么做。</p>
]]>
    </content>
</entry>
<entry>
    <title>让 Make 递归所有子目录</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/06/ea_make_eeeuooaea.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=476" title="让 Make 递归所有子目录" />
    <id>tag:blog.codingnow.com,2009://1.476</id>
    
    <published>2009-06-26T09:06:17Z</published>
    <updated>2009-06-26T09:19:30Z</updated>
    
    <summary>最近同事提了一个需求，想方便的调用一个工具校验所有的 xml 文件（包括子目录下的）。我想了一下，最简单的方法是用 Make 来辅助完成这件事情。 问题在于，怎样让 Make 递归的处理所有子目录。因为 GNU Make 默认的 wildcard 只能枚举出当前目录的文件，而不能递归下去。 求助于 Shell 指令当然是一种方法，如果只考虑 unix 环境，我会用 shell 去做。在 Windows 下也有 for 语句可以使用。但我不想处理复杂的平台差异。所以就在 Make 内置的函数里想办法了。...</summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="build tool" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p>最近同事提了一个需求，想方便的调用一个工具校验所有的 xml 文件（包括子目录下的）。我想了一下，最简单的方法是用 Make 来辅助完成这件事情。</p>

<p>问题在于，怎样让 Make 递归的处理所有子目录。因为 GNU Make 默认的 wildcard 只能枚举出当前目录的文件，而不能递归下去。</p>

<p>求助于 Shell 指令当然是一种方法，如果只考虑 unix 环境，我会用 shell 去做。在 Windows 下也有 for 语句可以使用。但我不想处理复杂的平台差异。所以就在 Make 内置的函数里想办法了。</p>
]]>
        <![CDATA[<p>一开始我认为 GNU Make 是个图灵完备的语言，有 call 和 eval 做函数模板和调用。if 函数可以用于递归终止。理论上能写递归就能解决别的语言解决的问题了。</p>

<p>后来试着在 makefile 里写了个简单的递归程序，结果让我很失望。Make (至少是 mingw32 的 make) 被我的小程序整挂了。输出了错误信息：</p>

<p>make: Interrupt/Exception caught (code = 0xc00000fd, addr = 0x7c92e8e7)</p>

<p>迫不得已，我使用了开子进程的方式。（这种方式在 Windows 下效率很低）</p>

<p>下面列出今天写的一段小程序，功能是递归遍历当前目录下的所有子目录。</p>

<pre>
.PHONY : all

all: 
    echo I'm in $(CURRENT_DIR)

CURRENT_DIR ?= .
FILES = $(wildcard $(CURRENT_DIR)/*)

define DIR_temp
.PHONY : $(1)
all: $(1)
$(1) :
    $$(MAKE) CURRENT_DIR=$$(CURRENT_DIR)/$(strip $(1))
endef

$(foreach e, $(FILES), \
  $(if $(wildcard $(e)/*), \
    $(eval $(call DIR_temp, $(notdir $(e))))))
</pre>
]]>
    </content>
</entry>
<entry>
    <title>近日小结</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/06/ueeoa.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=475" title="近日小结" />
    <id>tag:blog.codingnow.com,2009://1.475</id>
    
    <published>2009-06-22T15:30:08Z</published>
    <updated>2009-06-22T16:03:32Z</updated>
    
    <summary>项目进入一个比较繁忙的阶段。广州先后过来 5 个很有经验的同事一起做开发，各个方面都有条不紊的推进。让我轻松了许多。至少，我有更多的时间，专心写程序了。 目前，自我调整为一周六天工作，每天提早到上午十点半上班，并坚持在晚上两点前回家。维持了一个月后，感觉生物钟有所调整。周末也可以真正的谈的上休息了。 最近两个月居然去了三次电影院，如果不出意外，这周还要去一次看变形金刚。若放在半年前，我是无论如何没有这个兴致的。 新来的同事居然对抱石有兴趣，经过简单指导，已经有明显进步了。我的手指伤也好的差不多了。希望下个月的比赛可以去观摩一下。 另外，我们又多了几个桌游爱好者。前几天开了一局冰与火之歌，加了第二扩展，感觉很有意思。这两天订了一套 small world ，想必这种入门级的游戏可以更多吸引新人。可恶的是，RFTG 的第二扩展继续跳票。...</summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="杂记" />
            <category term="桌面游戏" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p>项目进入一个比较繁忙的阶段。广州先后过来  5 个很有经验的同事一起做开发，各个方面都有条不紊的推进。让我轻松了许多。至少，我有更多的时间，专心写程序了。</p>

<p>目前，自我调整为一周六天工作，每天提早到上午十点半上班，并坚持在晚上两点前回家。维持了一个月后，感觉生物钟有所调整。周末也可以真正的谈的上休息了。</p>

<p>最近两个月居然去了三次电影院，如果不出意外，这周还要去一次看变形金刚。若放在半年前，我是无论如何没有这个兴致的。</p>

<p>新来的同事居然对抱石有兴趣，经过简单指导，已经有明显进步了。我的手指伤也好的差不多了。希望下个月的比赛可以去观摩一下。</p>

<p>另外，我们又多了几个桌游爱好者。前几天开了一局冰与火之歌，加了第二扩展，感觉很有意思。这两天订了一套 small world ，想必这种入门级的游戏可以更多吸引新人。可恶的是，RFTG 的第二扩展继续跳票。</p>
]]>
        <![CDATA[<p>前段时间花了三个晚上研究了下魔方，终于弄明白了。不过速度很慢，平均还原时间要两分多钟。不过这是个练习记忆力和手指灵活度的好玩具，目前随身带着一个。</p>

<p>经过一年的挣扎，和反复研究，我终于买了个新鼠标。罗技的 G3 。今天到货，感觉不错。把原来用的那个 IE 4 扔掉了。（滚轮已坏，右键不灵，真佩服我自己还坚持用了几个月）</p>

<p>我们这里用 Linux 为主要（ 90% 时间）桌面的同事又有所增加。大家对 popo 没有非 Windows 版极其愤慨。尤其是连 Wine 都模拟不起来。终于，某人受不了了，开发了Pidgin 插件。整个开发历程真是段血泪史啊。</p>

<p>有多惨？想想一个完全没有通讯协议文档，而包格式无比繁杂的系统。全靠用 popo 跟开发人员聊天，一点点的把通讯协议整理出来。开发过程中，还弄挂了 popo 内部服务器若干次。好吧，我们现在内部公认 popo 的 client 写的很牛，不断更新也没闹出大故障。</p>

<p>话说两三年前，我强烈建议直接使用 XMPP 做新版的 popo 协议未果。悲剧啊。</p>

<p>目前插件支持功能十分有限，所以暂时不能开源和放出。不过，放出有意义么？有多少非公司的人在用 popo 和 Linux ？</p>

<hr />

<p>前几天一时冲动想买个小本玩玩，看中 Sony 的 P29H 。吃完晚饭冲到公司对面的 Sony 专卖店一问，只有 P17H 这款，冲动消费未果。回来后，看到有帖子说，用多了本颈椎容易出问题；结果第 2 天隔壁的同事去医院检查，还真检查出了颈椎病。</p>

<p>我还是坚持用台式机吧。</p>

<p>另外要多多出去野攀。不爬可以，帮人做保护，可以预防颈椎病。捉保护不是总得仰着头么？ :D</p>
]]>
    </content>
</entry>
<entry>
    <title>tcc 的一个 bug</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/06/tcc_bug.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=474" title="tcc 的一个 bug" />
    <id>tag:blog.codingnow.com,2009://1.474</id>
    
    <published>2009-06-04T18:13:11Z</published>
    <updated>2009-06-05T07:33:58Z</updated>
    
    <summary>tcc 是个好东西，我们的粒子系统把它当作可选模块，用于动态生成粒子控制代码。较之 lua 的版本，性能可以提高一个数量级（另外一个 gcc 版本的可选模块，会失去动态性）。为了引入这个库，我还好好研究了一下 LGPL 。 前段时间我还在抱怨 tcc 不能跑在 64 bit 环境下，结果今天因为查 bug ，去关心了一下新版本，发现 0.9.25 已经开始支持 64bit 了。 表扬到此为止，现在开始抱怨。...</summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="技术" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p><a href="http://bellard.org/tcc/">tcc</a> 是个好东西，我们的粒子系统把它当作可选模块，用于动态生成粒子控制代码。较之 lua 的版本，性能可以提高一个数量级（另外一个 gcc 版本的可选模块，会失去动态性）。为了引入这个库，我还好好研究了一下 LGPL 。</p>

<p>前段时间我还在抱怨 tcc 不能跑在 64 bit 环境下，结果今天因为查 bug ，去关心了一下新版本，发现 0.9.25 已经开始支持 64bit 了。</p>

<p>表扬到此为止，现在开始抱怨。</p>
]]>
        <![CDATA[<p>最近两天，我们有两个人陷入了一个诡异的 bug 中，把手头许多工作都停了下来。由于问题过于诡异，引发了许多猜想都一直未能确认。bug 似乎不是特定某段代码引起的，又牵扯进一大段复杂的 3d 渲染模块，稍稍改变一点上下文状态，就会在另一个不相关的代码中出问题。</p>

<p>所以我常说，单元测试或是面向测试的开发绝不是万能药。甚至有时候就是鬼扯。前段看到公司某项目写的大坨的所谓单元测试代码，我就觉得好笑。纯粹是浪费人力。用 1000 行测试 100 行的模块，不如肉眼去看。真正出了问题还找不出来。能起的作用无非也就是找出点拼写错误而已。</p>

<p>有那个时间写这么多测试代码，还不如多写点实际程序，提高点水平，减少 bug 产出。</p>

<p>闲话到此。说说最后定位出来的 bug 。</p>

<p>tcc 动态编译出来的函数，如果使用了浮点寄存器，有时候会在浮点栈上留下一个数字。即占用了 st(0) 。</p>

<p>而 gcc/msvc 通常在编译代码时，认为每次函数调用前后，浮点栈都是干净的，即有 8 个栈空间可用。</p>

<p>所以，在某些边界情况下，编译器生成的代码会企图用满 8 个浮点寄存器。如果中途调用了 tcc 生成的函数，会造成浮点栈溢出。浮点运算的结果不正常。</p>

<p>结论如此简单，但是两三个人耗在里面的时间真是让人心痛阿。我们这几天做的诸多猜测，和各种实验就不详细写了。</p>

<hr />

<p>末了，我研究了一晚上 tcc 的  source ，打算打个补丁，不过太晚了，暂时还没有头绪。本来想先写封 email 报告一下 bug 。打开了源码包里的 TODO 文件。感觉脑袋后面都是竖线，最前面赫然写着：</p>

<p>Bugs:</p>

<ul>
<li>FPU st(0) is left unclean (kwisatz haderach). Incompatible with  optimized gcc/msc code</li>
</ul>

<p><a href="http://lists.gnu.org/archive/html/tinycc-devel/2008-01/msg00002.html">在 mailling list 上的相关讨论帖</a> 。</p>

<p>教训：用开源代码前，一定要熟读 bug list 。</p>
]]>
    </content>
</entry>
<entry>
    <title>《链接、装载与库》书评</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/06/link_loader.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=473" title="《链接、装载与库》书评" />
    <id>tag:blog.codingnow.com,2009://1.473</id>
    
    <published>2009-06-01T12:14:38Z</published>
    <updated>2009-06-01T12:53:27Z</updated>
    
    <summary><![CDATA[ 今年二月份拿到这本书的电子稿时，还不是现在这个名字。 《程序员的自我修养》这个名字听起来比原来的那个名字感觉好一些，但又让人感觉有点不知所谓。还是副标题直接：《链接、装载与库》。我更愿意接受这样的一个名字，有如那本多年前读过的英文经典：《Linkers &amp; Loaders》。 那段时间很忙，一直到现在都是。书稿我压了很久，直到有一天，博文的朋友说，约个时间和 Fenng 、俞甲子等杭州的程序员碰头聚一下。我连夜开始读书稿。不然，见面了谈起这本书来，说不出所以然多不好意思。...]]></summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="读书" />
            <category term="技术" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p><a href="http://www.douban.com/subject/3652388/"><img src="http://t.douban.com/mpic/s3724604.jpg" style="float:left;padding:0 20px 20px 0;border:0"/></a></p>

<p>今年二月份拿到这本书的电子稿时，还不是现在这个名字。</p>

<p>《程序员的自我修养》这个名字听起来比原来的那个名字感觉好一些，但又让人感觉有点不知所谓。还是副标题直接：《链接、装载与库》。我更愿意接受这样的一个名字，有如那本多年前读过的英文经典：《Linkers &amp; Loaders》。</p>

<p>那段时间很忙，一直到现在都是。书稿我压了很久，直到有一天，博文的朋友说，约个时间和 Fenng 、俞甲子等杭州的程序员碰头聚一下。我连夜开始读书稿。不然，见面了谈起这本书来，说不出所以然多不好意思。</p>
]]>
        <![CDATA[<p>书很厚，但是我看的很快。可能是因为内容很熟悉吧。大约花了一个通宵就基本读完了。写的很细，当时快速翻过去的时候，发现几处小笔误，由于我的 pdf 阅读器不能做评注，就忘记了。</p>

<p>我自己写过书，知道写书的艰辛。</p>

<p>很简单的道理，自己本以为明白的很，要写出来让人明白，是件非常不容易的事。前几年，我在 blog 上随手写的一个<a href="http://blog.codingnow.com/2006/11/windows_unix_dynamic_library.html">关于链接方面的帖子</a>，就引起了无穷无尽的争议。Blog 尚可随意为之，可出版成白纸黑字，必然战战兢兢。一旦写错点什么，很容易误导许多人。见过俞甲子以后，我相信他和我一样，是个从内心对读者负责的人。</p>

<p>关于链接，装载等问题。是操作系统中很基础很重要的一个部分。从特定的操作系统的解决方案：分析 Windows  的 PE 或是 Linux 的 ELF 的结构，可以理解操作系统是如何让一段代码工作起来、怎样让不同的二进制模块协同工作的。这是理解整个系统的一个起点。</p>

<p>一方面，我们理解系统如何去做，是为了悟到为何这样去设计。了解了为什么，反过来更能理解怎样去做。我为我的系统做过一段时间的 loader ，为了隔离操作系统的差异，来解决二进制的代码复用。不少朋友问我怎样做到的。其实答案很简单：自己来写 loader 。理解操作系统怎样干的后，这并不是难事。至于需不需要越俎代庖替代操作系统去做，那就需要对这方面更多的理解了。这绝不是简单的是否应该重新发明轮子的问题。</p>

<p>好吧，说起重造轮子的话题，怕又引起无穷尽的争议。但有一点应该是有比较少的争议的。你可以不自己造轮子，但应该了解轮子的构造，而且越详尽越好。这是程序员的自我修养吧。</p>

<p>另一方面，我们对自己每天用的系统多一些了解，那是百利而无一害。即使从最实用主义的角度看，碰到一些表面上看起来无可适从的 bug ，也能分析清楚。比如，<a href="http://blog.codingnow.com/2009/03/libstdcpp_dlclose_crash.html">我前段时间碰到的那个诡异的 bug</a> 。</p>

<hr />

<p>前几天收到博文寄过来的书，本答应周老师写一篇书评，一直没能动笔。端午节休假，终于把这笔欠帐还上了。:) 这本书是本好书，我会放在书架上，相信总有需要的时候拿出来查阅。</p>
]]>
    </content>
</entry>
<entry>
    <title>lua 中判断字符串前缀</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/05/lua_string_prefix.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=472" title="lua 中判断字符串前缀" />
    <id>tag:blog.codingnow.com,2009://1.472</id>
    
    <published>2009-05-26T14:40:22Z</published>
    <updated>2009-05-27T12:04:39Z</updated>
    
    <summary>一个 lua 的小技巧 在写 lua debugger 的时候，我需要判断一个字符串的前缀是不是 &quot;@&quot; 。 有三个方案： 比较直观的是 string.sub(str,1,1) == &quot;@&quot; 感觉效率比较高的是 string.byte(str) == 64 或者是 string.find(str,&quot;@&quot;) == 1 我推荐第三种。（注：在此特定运用环境下。因为用于判定 source 的文件名，大多数情况都是 @ 开头。如果结果为非，则性能较低） 第一方案 string.sub 会在生成子串的时候做一次字符串 hash ，感觉效率会略微低一些。 第二方案效率应该是最好，但是需要记住 @ 的 ascii 码 64 。如果前缀是多个字符也不适用。...</summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="lua与虚拟机" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p>一个 lua 的小技巧</p>

<p>在写 lua debugger 的时候，我需要判断一个字符串的前缀是不是 "@" 。</p>

<p>有三个方案：</p>

<ol>
<li>比较直观的是 string.sub(str,1,1) == "@"</li>
<li>感觉效率比较高的是 string.byte(str) == 64</li>
<li>或者是 string.find(str,"@") == 1</li>
</ol>

<p>我推荐第三种。（注：在此特定运用环境下。因为用于判定 source 的文件名，大多数情况都是 @ 开头。如果结果为非，则性能较低）</p>

<p>第一方案 string.sub 会在生成子串的时候做一次字符串 hash ，感觉效率会略微低一些。</p>

<p>第二方案效率应该是最好，但是需要记住 @ 的 ascii 码 64 。如果前缀是多个字符也不适用。</p>
]]>
        

    </content>
</entry>
<entry>
    <title>lua 调试器制作注意</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/05/lua_debugger.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=471" title="lua 调试器制作注意" />
    <id>tag:blog.codingnow.com,2009://1.471</id>
    
    <published>2009-05-25T13:27:16Z</published>
    <updated>2009-05-26T04:10:08Z</updated>
    
    <summary>前两年写过一个 lua 的调试器，blog 上有截图 不过调试器设计的关键不在于界面，在于调试协议。前两年的那个是设计的不完整的。 最近同事强烈要求引擎提供一个强力的调试工具，虽然我个人不太依赖调试去写代码。甚至认为，经过反复调试才正确工作的代码不是好代码。不过周末还是花了点时间重新制作了一个 lua 调试器。 中间发现一些问题，非常让人吐血。列在这里，做个记录。...</summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="lua与虚拟机" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p>前两年写过一个 lua 的调试器，<a href="http://blog.codingnow.com/2006/11/lua_debugger.html">blog 上有截图</a></p>

<p>不过调试器设计的关键不在于界面，在于调试协议。前两年的那个是设计的不完整的。</p>

<p>最近同事强烈要求引擎提供一个强力的调试工具，虽然我个人不太依赖调试去写代码。甚至认为，经过反复调试才正确工作的代码不是好代码。不过周末还是花了点时间重新制作了一个 lua 调试器。</p>

<p>中间发现一些问题，非常让人吐血。列在这里，做个记录。</p>
]]>
        <![CDATA[<p>一开始我比较担心 lua 的 debug hook 会降低运行性能。所以考虑的优化比较多。</p>

<p>都说提前优化是万恶之源，此话绝对不假。可挡不住的诱惑是，制作一个几乎不影响运行性能的调试器，更为实用。而且这个东西我构思了好几年，优化策略也思考再三，不算提前优化吧。</p>

<p>基本优化策略是把被调试程序分成三种调试状态：</p>

<ol>
<li><p>无须 hook 的状态。这个状态下，没有设置断点，lua 程序可以全速去跑。只要定期查询一下，调试器有没有指令输入即可。( 和 gdb 一样，这个调试器支持且暂时仅支持远程调试）这个轮询，可以放在程序主循环内，定期跑一下即可。几乎不会影响正常的程序运行。这样，我可以随时 attach 到正在工作的 lua 进程上。</p></li>
<li><p>高密度的 hook 状态，使用 lua debug hook 的 line 模式。每条 lua 指令都被 hook ，用来检测断点的工作。</p></li>
<li><p>低密度的 hook 状态。仅使用 lua debug hook 的 call/return 模式。这个模式下，仅仅返回进入和返回被 hook ，可以做一些粗略的判断。</p></li>
</ol>

<p>实现的时候发现几个问题。</p>

<p>我原来的计划中，使用 call/return 的 hook 消息，监视 lua 运行所在的源文件/ 函数。 一旦发现没有断点，则可以使用第三模式运行，提高被监控状态的运行效率。</p>

<p>当然，如果所有断点都被 disable ，则切换到第一状态。</p>

<p>事实上，简单的在 call/return  的 hook 内获取上级函数的 source 是不够的。因为，lua 的 call hook 发生的时候，已经进入被调用函数；而 lua 的 return hook 发生，尚还停留在调用函数的最后一行。也就是说，可能没有机会回到被调用者。</p>

<p>比如在 a.lua 中写上两个函数调用 ：</p>

<p>foo1()</p>

<p>foo2()</p>

<p>而把 foo1 和 foo2 定义在 b.lua 中。如果仅仅 hook call/return 。会发现 foo1() 和 foo2() 调用期间，从 hook 中，没有回到 a.lua 。假设在 foo2() 上设置断点，就没有机会断下来。</p>

<p>想来想去，补救的方法是：当发生 return 或 tail return 时，多看一级堆栈，获得调用者的 source 名。不过这样还是有点问题，有可能调用者是从 C 或是一段运行期产生的代码中过来，依然回溯不到正确的位置。正确的做法是一直回溯到可识别的源代码文件名。（ @ 开头的字符串）</p>

<p>最后再查表判定是否需要切换 hook 的 mask 状态。</p>

<p>另一点是关于 lua 的 tail return 的，return 和 tail return 是分开的事件，在实现 step over 的功能时务必小心。</p>

<p>关于无效代码行的判定没有想到特别好的方案。即设置断点时，如果设置在无效行上，应该向上移动到有效位置。虽然 lua 的 debug 模块提供了一些相关的支持，但是比较有限。靠监控运行的当前函数来反复匹配合适的断点位置，代码写起来会过于繁琐。</p>

<p>在调试器中查看变量的值，最好注意一下潜在的副作用。主要是 metatable 造成的。用 rawget 去取 table 里的数据更靠谱一些，需要小心的是是 tostring 的 meta 方法调用。</p>

<p>lua 的高阶用法中，往往会由一段代码生成新的代码运行。让调试器识别这种情况，并给予支持，会带来许多方便。（主要是格式化源码，并对 unix 和 dos 的回车做兼容比较麻烦）</p>

<hr />

<p>周六一天本来把调试器已经写完，后来发现一些隐藏的 bug 。越看代码越不顺眼，然后周日推翻重写了一次。</p>

<p>新的版本主要是建立起一个调试状态机。把调试器的各种状态严格区分。比如运行态和阻塞交互状态。以及大状态下的小状态划分。</p>

<p>因为后面会做一个图形交互前端，对调试指令的协议定义要求比较严格。文档也仔细研究过，参考了 gdb 的协议。</p>

<hr />

<p>做完的感觉就是累。没想到后面有没完没了的小需求加进来。幸亏中途一咬牙重写了。否则肯定在周末是完成不了的。不算 socket 通讯部分（以前自己实现的一个模块），暂时有 1000 行 lua 代码吧，比第一个版本简洁很多，但比我预期代码规模大一些。也不能算太好看。</p>

<hr />

<p>btw，我在选择调试器默认端口时，选了个 3563 。16 进制为 0xdeb 。
昨天 Sean 同学告诉我，这个是个知名端口： 乃 Wacom C 的调试器端口。吼吼，真是英雄所见略同。</p>
]]>
    </content>
</entry>
<entry>
    <title>X Window 的 Resize 处理</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/05/x_window_resize.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=470" title="X Window 的 Resize 处理" />
    <id>tag:blog.codingnow.com,2009://1.470</id>
    
    <published>2009-05-20T13:30:13Z</published>
    <updated>2009-05-20T13:46:54Z</updated>
    
    <summary>程序员在陌生领域工作时，都想寻求范例。看起来，我们都很依赖 Meme Machine 。 可惜的是， X Window 领域的直接针对 XLib 编程的范例太少。偶尔碰到点问题都让人很痛苦。只有反复研究文档了。 我的程序在处理 Resize 消息时老是不正确，仔细阅读文档后，发现是以前理解有问题。 起先，我为窗口消息注册了 ResizeRedirectMask ，然后在消息循环中就可以得到一个 ResizeRequest 消息。然后，我处理这个消息，但结果总是不对。 今天研究了一下，发现 ResizeRedirectMask 会导致 no further processing is performed 。也就是说，通知你 resize 请求后，X 系统就不管你了。至于还差什么事情要做（比如改变客户区大小），我也不知道该怎么做才完全正确。（如果在 Windows 里，应该是调用 DefaultWindowProc ) 不过解决方法其实也很简单，不用注册 ResizeRedirectMask ，而注册 StructureNotifyMask 。在窗口改变的时候， X...</summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="X Window" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p>程序员在陌生领域工作时，都想寻求范例。看起来，我们都很依赖  Meme Machine 。</p>

<p>可惜的是， X Window 领域的直接针对 XLib 编程的范例太少。偶尔碰到点问题都让人很痛苦。只有反复研究文档了。</p>

<p>我的程序在处理 Resize 消息时老是不正确，仔细阅读文档后，发现是以前理解有问题。</p>

<p>起先，我为窗口消息注册了 ResizeRedirectMask ，然后在消息循环中就可以得到一个  ResizeRequest 消息。然后，我处理这个消息，但结果总是不对。</p>

<p>今天研究了一下，发现 ResizeRedirectMask 会导致  no further processing is performed 。也就是说，通知你 resize 请求后，X 系统就不管你了。至于还差什么事情要做（比如改变客户区大小），我也不知道该怎么做才完全正确。（如果在 Windows 里，应该是调用 DefaultWindowProc )</p>

<p>不过解决方法其实也很简单，不用注册 ResizeRedirectMask ，而注册 StructureNotifyMask 。在窗口改变的时候， X 会发一个  ConfigureNotify 。因为这个只是 Notify ，所以 X 会把所有它应该做的事情做的周全。</p>
]]>
        <![CDATA[<p>btw, Sean 同学推荐我用一下 <a href="http://xcb.freedesktop.org/">XCB</a> ，因为 XLib 用起来实在是太头痛了。这是个替代物。</p>
]]>
    </content>
</entry>
<entry>
    <title>树结构的管理</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/05/tree.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=469" title="树结构的管理" />
    <id>tag:blog.codingnow.com,2009://1.469</id>
    
    <published>2009-05-10T13:13:48Z</published>
    <updated>2009-05-11T02:53:19Z</updated>
    
    <summary>要写过多少代码才能得到哪怕一点真谛？ 多少年过来，我在潜意识的去追求复杂的东西。比如我自幼好玩游戏，从小到大，一直觉得玩过的游戏过于简单（无论是电子游戏还是桌面游戏），始终追寻更复杂规则的游戏，供我沉浸进去。或许是因为，有了更高的理解和控制复杂度的能力，就可以更为轻松的驾御复杂性。 这很好的解释了 2000 年到 2004 年我对 C++ 的痴迷。还有对设计模式的迷恋。 Eric S. Raymond 说：尽量不要去想一种语言或操作系统最多能做多少事情，而是尽量去想这种语言或操作系统最少能做的事情——不是带着假想行动，而是从零开始。禅称为“初心”（beginner&apos;s mind）或者叫“虚心”(empty mind) 。 代码写多了，问题见过了，甚至是同一问题解决多了。模式这种东西自在心底，不必拿出来。时时的从零去想，总能重新明白一些道理。 为什么说语言重要也不重要，算法和数据结构重要也不重要。对要解决的问题的领域的理解很重要（即明白真正要做什么）。理解了，我们才可以用面向对象，用模式去套问题；可理解了，我们又不真的需要这些繁杂的抽象。 闲话放一边，今天想谈谈树结构的管理。...</summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="lua与虚拟机" />
            <category term="随笔" />
            <category term="游戏开发" />
            <category term="语言与设计" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p>要写过多少代码才能得到哪怕一点真谛？</p>

<p>多少年过来，我在潜意识的去追求复杂的东西。比如我自幼好玩游戏，从小到大，一直觉得玩过的游戏过于简单（无论是电子游戏还是桌面游戏），始终追寻更复杂规则的游戏，供我沉浸进去。或许是因为，有了更高的理解和控制复杂度的能力，就可以更为轻松的驾御复杂性。</p>

<p>这很好的解释了 2000 年到 2004 年我对 C++ 的痴迷。还有对设计模式的迷恋。</p>

<p>Eric S. Raymond 说：尽量不要去想一种语言或操作系统最多能做多少事情，而是尽量去想这种语言或操作系统最少能做的事情——不是带着假想行动，而是从零开始。禅称为“初心”（beginner's mind）或者叫“虚心”(empty mind) 。</p>

<p>代码写多了，问题见过了，甚至是同一问题解决多了。模式这种东西自在心底，不必拿出来。时时的从零去想，总能重新明白一些道理。</p>

<p>为什么说语言重要也不重要，算法和数据结构重要也不重要。对要解决的问题的领域的理解很重要（即明白真正要做什么）。理解了，我们才可以用面向对象，用模式去套问题；可理解了，我们又不真的需要这些繁杂的抽象。</p>

<p>闲话放一边，今天想谈谈树结构的管理。</p>
]]>
        <![CDATA[<p>这个问题来自游戏引擎里对对象的管理。有时也出现在 GUI 模块中。</p>

<p>我以前总结过，所谓面向对象，就是可以用统一的方法对不同的对象进行同样的操作。而这个过程，需要我们把对象的同质引用放到一个容器中。这个容器，绝大多数情况下，由一个集合结构即可胜任。</p>

<p>而有些复杂的问题领域中，我们又需要树型结构的容器。有时是为了优化（比如在 3d 引擎中，用树结构描述对象的位置相对关系，以及用于裁减），有时是为了分类（比如把所有 npc 放到一颗子树下，而把 item 放到另一颗下）。</p>

<p>本质上，容器的处理异于普通对象，而树结构即容器之容器。把容器之容器看成一个单体，有利于问题的简化。</p>

<p>处理这种复杂数据结构，动态语言相对于 C/C++ 这样的静态语言，有比较明显的优势。但是在性能方面又有明显的劣势。权衡之下，我们需要做的是采用 C 去实现底层细节，而动态语言做高层管理，并控制粒度，减少控制频率。</p>

<p>据我个人观察和实践，虽然最终游戏 engine 管理的树结构非常繁杂。但 C/C++ 部分运转起来之后，需要特别控制的节点层次并不多。</p>

<p>但是，若想充分利用动态语言的动态性，在子树构建阶段，又非常有可能触及比较复杂的树层次。只到构建子树完毕，大多数中间层次和节点永远都不会再被特别控制。</p>

<p>具体的例子有 3d 粒子系统，人物换装系统等等。如果用动态语言去描述那些小部件的搭建过程会比（在 C/C++ 里）较轻松。但是搭建完毕后，可能持续引用的节点并不多。大部分中间节点留在 C 层自我运转就足够了。</p>

<p>这就是我上面说的粒度问题。在构建的局部阶段，我们需要局部的细粒度。而在全局控制阶段，我们则需要全局的粗粒度。</p>

<p>如果一贯的保持细粒度，对于动态语言很可能发生性能问题。而增加动态语言和 C 底层的结合度，也更有对象生命期管理的麻烦事。如果只是麻烦倒也罢了，随之带来的 gc 的负担往往也不可小窥。</p>

<hr />

<p>我现在的解决方案：</p>

<p>把树节点分为匿名和具名两类。匿名节点只在构建期对上下文可见。具名节点，可以以路径名（相对或绝对）方式引用。</p>

<p>如果以 lua 为实作，接口类似这个样子。</p>

<pre>
create()
with(function(self) 
  -- do something with self
end)

create("name")
with("name",function(self)
  -- do something with node "name"
end)
</pre>

<p>create 方法可以在当前的位置创建一个子节点，可以给这个子节点起名，也可以匿名。</p>

<p>with 方法可以引用一个节点（如果给出名字，则找到具名节点，如果不给名字，则引用最近创建的一个匿名节点），并执行一个代码块（以 closure 方式给出），在这个代码块中可以引用这个节点对象 self ，操作 self 的各种属性和方法。但是 self 不可以传递到代码块之外。即不能被外面引用。这点在运行期通过锁机制保证。如果想长期引用一个节点，必须通过节点名。</p>

<p>除此之外，提供两个销毁节点的方法：</p>

<p>clear (name) 清除具名节点下的所有子节点。匿名节点不提供清理方法。</p>

<p>delete (name) 删除具名节点本身以及其下的子树，同样对匿名节点无效。</p>

<p>不提供任何枚举子节点和遍历子树的接口。（那些是 C 层次的事情）</p>

<p>为什么这样设计？仅仅这几个接口足够了吗？</p>

<p>这样，我们保证了动态语言层和 C 层关系的足够简单。尤其是规避了复杂的生命期管理。所有的节点都通过 create 构建，到 clear/delete 消亡。动态语言层有能力得到所有生命期信息。</p>

<p>而动态语言层没有移动子树和直接的持久引用特定树节点对象的能力，这向 C 层担保了绝对不会有悬空指针。具体到使用 lua 做封装时，我们简单的用 lightuserdata 引用 C 对象即可。</p>

<p>为什么提供 clear 和 delete 两种销毁节点的形式？这是因为我们没有枚举和遍历的接口，没有这两个接口，可以方便维持动态语言层上的对象粒度。</p>

<p>clear 用于插槽(slot) ，比如人物换装。模型的手上就可以有一个叫 hand （或 left_hand）的 slot ，供我们把武器的模型插上去。更换武器时，clear hand 这个 slot ，然后在其上重新创建新对象即可。</p>

<p>名字相对路径的支持，使得在特定位置创建子树可以做为通用模块。</p>

<p>delete 用于动态生成的对象本身。比如我们可以把场景中的 npc 挂在 root.npc 的子树下。再为每个 npc 以 id 为名创建节点。销毁 npc 即 delete root.npc.xxx (xxx 为 id)</p>

<p>匿名节点的设计，可以把大量复杂的子树构建过程交给动态语言去完成。</p>

<p>with 方法可以为上层程序员提供一个安全屏障。最主要是节点有效的保障。</p>

<hr />

<p>上周末有同事问我，要不要增加移动子树的接口。比如有时我们需要把主角模型从一个地图层移到另一个地图层（比如从高楼上跳下）。我觉得这可能是个伪需求。</p>

<p>显示上的约束条件（模型依附在那个位置），和场景树的管理应该分离。比如不可以因为人物围着桌子跑，就把人的模型挂在桌子的坐标系内。</p>

<p>但另一些时候，我们需要让人物跨场景，则完全可以在旧场景中删除，然后在新场景中创建。在显示的表现上也可以做到不让用户察觉。</p>

<p>当然，也可能因为优化需要，或是实现简化的需要，我们需要增加这样一个方法。</p>

<p>move (name, parent)</p>

<p>把一个具名节点移动到另一个具名节点之下。</p>

<p>这最后一个方法或许是个可选项。</p>

<hr />

<p>今天写了 400 多行 lua 代码，始终没什么感觉。看来周末就是应该休息一下。</p>

<p>最近想提高一点团队的工作效率，大家开会决定，以后每天最晚到岗时间提前到上午 10:30 。对我还是有点痛苦的  :) 我这几年，一直都习惯于中午再起床了。不过坚持了一周，感觉还行。也自然而然的把自己的下班时间提前到了 1:00 左右，睡眠似乎没受太大影响。</p>

<p>另，为近期的工作安排花了日程图，大大的挂在墙上。好象花点时间搞点形式主义出来，还真有点效果。 :D</p>
]]>
    </content>
</entry>
<entry>
    <title>在文本模式下显示中文</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/05/chinese_char_in_text_mode.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=468" title="在文本模式下显示中文" />
    <id>tag:blog.codingnow.com,2009://1.468</id>
    
    <published>2009-05-09T09:53:28Z</published>
    <updated>2009-05-09T10:31:48Z</updated>
    
    <summary>办公室里我有两台桌面机，一台装的 Windows ，另一台装的 freeBSD 和 Ubuntu 双系统。上班的时候，两台机器我都开着，跑 freeBSD 的时候比较多。 在 freeBSD 下，我很少进 X 。主要是写服务器程序，或者是用来 ssh 到别的服务器上做管理。纯文本模式很清爽，速度很快，让人心情愉快。 唯一的烦恼是，有时候屏幕上有那么几个汉字显示不出来。有时是代码里的中文注释（所以我本人虽然英文极滥，也坚持用英文写注释），有时是别的机器上的一些文件名。 以前有同学向我推荐 zhcon ，类似以前 dos 下的中文系统。可这个玩意性能极低。用它我还不如直接用 X 呢。 周末，我花了两个小时写了个小程序，算是自己的解决方案。...</summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="技术" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p>办公室里我有两台桌面机，一台装的 Windows ，另一台装的 freeBSD 和 Ubuntu 双系统。上班的时候，两台机器我都开着，跑 freeBSD 的时候比较多。</p>

<p>在 freeBSD 下，我很少进 X 。主要是写服务器程序，或者是用来 ssh 到别的服务器上做管理。纯文本模式很清爽，速度很快，让人心情愉快。</p>

<p>唯一的烦恼是，有时候屏幕上有那么几个汉字显示不出来。有时是代码里的中文注释（所以我本人虽然英文极滥，也坚持用英文写注释），有时是别的机器上的一些文件名。</p>

<p>以前有同学向我推荐 zhcon ，类似以前 dos 下的中文系统。可这个玩意性能极低。用它我还不如直接用 X 呢。</p>

<p>周末，我花了两个小时写了个小程序，算是自己的解决方案。</p>
]]>
        <![CDATA[<p>我们知道，VGA 的文本模式，是可以自己换字模的。用 vidcontrol -f 即可。</p>

<p>文本模式可以显示 256 个不同字符，一般用不了那么多。比如 freeBSD 的 syscons 就用了 4 个字符去画鼠标光标。原理就是动态修改字模。有兴趣的同学可以参考 /usr/src/sys/dev/syscons/scvgarndr.c</p>

<p>最简单的办法是用 [128,255] 的 128 个字符临时显示汉字，可以支持 64 个不同的汉字。考虑到鼠标光标占用了 4 个位置。其实是 62 个汉字。</p>

<p>一般也够用了。</p>

<p>我写了几行小程序干这件事情，支持管道操作。从标准输入读入 UTF-8 文本，发现是非 ASCII 集合中的，就临时生成对应的点阵字模（通过 freetype ），把这些字符映射到 [128,255] 之间，输出到标准输出。</p>

<p>以上基本满足我自己的需要。</p>

<hr />

<p>如果有精力，我希望可以嵌入 syscons 中。比如按下 Scroll Lock 滚屏时，同时检查屏幕上是否有中文，有则做对应转换。在 Scroll Lock 模式下，不必考虑跟 Ascii 冲突的问题，可用的字符是全部 256 个。如果还不够用，可以定时切换字模，并不暂时不可显示的字符做特殊处理（标记为 ? ）</p>

<p>再牛 X 一点，可以嵌入一个中文输入法。</p>

<p>或者还有第 2 方案，就是改造 less 。让 less 支持在控制台下显示汉字，如果汉字一次显示不完，暂停就是了。</p>

<hr />

<p>ps. 莫向我索要代码。只是想应付自己的需要，写的太乱，各种路径也是写死的，不好意思拿出来丢人。需要自己写的，关键技术（更换字模）除了参考  /usr/src/sys/dev/syscons/scvgarndr.c 外，我还参考了： /usr/src/usr.sbin/vidcontrol/vidcontrol.c 。不过几十行程序而已，自己动手，丰衣足食。还是开源系统好啊。:D</p>

<p>另外，如果有同学知道世界上已经存在了上面我说的东西（比如按下 Scroll Lock 就把屏幕上的汉字显示出来），请告之。:D 若是有同学想写一个，我们可以私下交流一下。</p>
]]>
    </content>
</entry>
<entry>
    <title>回顾 Forth</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/05/forth.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=466" title="回顾 Forth" />
    <id>tag:blog.codingnow.com,2009://1.466</id>
    
    <published>2009-05-06T12:20:04Z</published>
    <updated>2009-05-06T12:42:13Z</updated>
    
    <summary>第一件事就是没有钩子。不要留一个接口，想着未来的什么时候当问题变化时插入一些代码，因为问题会以你不能预见的方式变化。反正这个成本肯定是浪费了。不要预测，只解决你眼前的问题。by Charles Moore (Forth 之父) 今天也是机缘巧合，莫名其妙的翻出老资料温习 Forth 了。我想是个心结吧。19 年前，我痴迷于 Forth ，只看到了皮毛；13 年前，我进入大学的第一年，在校图书馆借出的第一本书，就是《Forth 语言》，读书笔记写了 20 多页。 只到今天，我才有机会，有能力，去仔细探究 Forth 的深层思想。当然，由于时间有限，几个小时的阅读，也只算是初窥门径。原本是想研究下 Forth 的系统实现，对同事正在设计的 3d 粒子系统，提供一些建议的。 碰巧又读到 Charles Moore 在 99 年的访谈稿 1x Forth ，颇多感慨。题头那段话，我在一周前刚好苦口婆心的对一同事说过，只差几个字而已。...</summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="lua与虚拟机" />
            <category term="随笔" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p>第一件事就是没有钩子。不要留一个接口，想着未来的什么时候当问题变化时插入一些代码，因为问题会以你不能预见的方式变化。反正这个成本肯定是浪费了。不要预测，只解决你眼前的问题。by Charles Moore (Forth 之父)</p>

<p>今天也是机缘巧合，莫名其妙的翻出老资料温习 Forth 了。我想是个心结吧。19 年前，我痴迷于 Forth ，只看到了皮毛；13 年前，我进入大学的第一年，在校图书馆借出的第一本书，就是《Forth 语言》，读书笔记写了 20 多页。</p>

<p>只到今天，我才有机会，有能力，去仔细探究 Forth 的深层思想。当然，由于时间有限，几个小时的阅读，也只算是初窥门径。原本是想研究下 <a href="http://www.forthchina.com/articles/shixian/B01/00.htm">Forth 的系统实现</a>，对同事正在设计的 3d 粒子系统，提供一些建议的。</p>

<p>碰巧又读到 Charles Moore 在 99 年的访谈稿 <a href="http://www.forthchina.com/articles/yanjiu/20040726.htm">1x Forth</a> ，颇多感慨。题头那段话，我在一周前刚好苦口婆心的对一同事说过，只差几个字而已。</p>
]]>
        <![CDATA[<hr />

<p>最近很忙，既然晚上不能睡的再晚了，只好早上早点起。现在改为 10:20 起床了，比过往的 12:20 足足提早了 2 小时。感觉每天可用的时间的确长了许多。</p>

<p>晚上回家的时候，小巷子里总有两条狗。见我走过来便会汪汪狂吠，然后冲过来。头几次我心里还有点点害怕。次数多了也就习惯了。通常它们会冲到我跟前一米左右缓下来，跟着我一直走到家门口。跟它们打招呼也不大搭理，也就是用眼神盯着。</p>

<p>我想起同事养的一只贵宾。我每天都煮好两鸡蛋带到公司，饿的时候吃掉。上次体检 B 超的时候医生说我胆固醇太高，我便尽量不吃蛋黄了。若是那条狗在公司，我只要一敲鸡蛋，它就飞奔到我的座位前坐下，摇尾乞怜。已经不只听一个养狗的人说，鸡蛋黄对狗有莫大的吸引力了。</p>

<p>这两天，我吃完鸡蛋，都把蛋黄带着。夜路上再碰见那两条狗儿，就抛给它们。</p>

<p>这个时候，我居然会想起奥贝斯坦。</p>
]]>
    </content>
</entry>
<entry>
    <title>今天遭遇太好笑的房东</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/05/niioaooeiaae.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=465" title="今天遭遇太好笑的房东" />
    <id>tag:blog.codingnow.com,2009://1.465</id>
    
    <published>2009-05-05T14:37:49Z</published>
    <updated>2009-05-05T15:42:49Z</updated>
    
    <summary>前段工作太忙，有天中午停水，我开了水龙头见没水忘记关，结果水淹了洗手间。地漏不畅，所以漏水到了楼下。 晚上晚饭时就接到楼下邻居愤怒的电话，急忙从公司奔回家看。看人家家里到处是水，心有不忍。也是想把事情早点解决掉，最后当晚提了一万元作为赔偿。 老实说，按实际损失（楼下家里并没有怎么装修），我心里价位也就是 3000 块最多了。只是我自己犯了错误，感觉对不起邻居，说赔多少就爽快的答应了。大家还是合合气气做邻居。这段时间进进出出，还笑着打打招呼。 没想到这房东不乐意了。觉得我工资卡上的钱是天上掉下来的，说拿就拿出来。成天想着让我也给她一点。理由是地板被泡坏了。老天，水是从洗水间直接漏下去的，顺着楼下那家的天花板滴的满屋子。 我们家当天，客厅地板一点毛病都看不出来。这么多天下来，地板也丝毫没有异样。 我这人就这毛病，是我的责任，我绝对不推卸，无中生有的事情让我去承担，那就没那么容易。...</summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="杂记" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p>前段工作太忙，有天中午停水，我开了水龙头见没水忘记关，结果水淹了洗手间。地漏不畅，所以漏水到了楼下。</p>

<p>晚上晚饭时就接到楼下邻居愤怒的电话，急忙从公司奔回家看。看人家家里到处是水，心有不忍。也是想把事情早点解决掉，最后当晚提了一万元作为赔偿。</p>

<p>老实说，按实际损失（楼下家里并没有怎么装修），我心里价位也就是 3000 块最多了。只是我自己犯了错误，感觉对不起邻居，说赔多少就爽快的答应了。大家还是合合气气做邻居。这段时间进进出出，还笑着打打招呼。</p>

<p>没想到这房东不乐意了。觉得我工资卡上的钱是天上掉下来的，说拿就拿出来。成天想着让我也给她一点。理由是地板被泡坏了。老天，水是从洗水间直接漏下去的，顺着楼下那家的天花板滴的满屋子。</p>

<p>我们家当天，客厅地板一点毛病都看不出来。这么多天下来，地板也丝毫没有异样。</p>

<p>我这人就这毛病，是我的责任，我绝对不推卸，无中生有的事情让我去承担，那就没那么容易。</p>
]]>
        <![CDATA[<p>今天房东晚上到我家里静坐来了。相当郁闷。</p>

<p>我坐那听她叽叽喳喳，感觉这人还真不可理喻。尽说些自相矛盾的话，一开始我是心里发笑，最终还是把笑容写在脸上了。只是不好说破。</p>

<p>末了，还被她甩上一句，今天你肯定恨死我了。我这一走，你把我们家冰箱啊什么的弄坏掉，然后不租房子了走人，我可就亏大了。赔偿的事情就算先不说，今天先加一万押金，防止我做坏事。让我还真是哭笑不得。</p>

<p>什么叫自相矛盾？</p>

<p>先说，楼下那点损失算什么？其实干了啥都没有，根本不用赔什么钱。
然后说，你看楼下都那样了，我家这地板能好么？最少赔 4,5 万呐。</p>

<p>然后我们谈是否续租的问题：（我是 6 月到期，并且有 5000 押金在她那）</p>

<p>先说，你这不租了，我损失多大啊。再找房客要时间，而且房子旧了，租金还要便宜。（一定要继续住，而且房租还得加）
然后说，我这房子不愁租的，我打听过了，换个房客，房租一年至少可以多一万。（您租不租无所谓的，不求您）</p>

<p>那么续约呢？</p>

<p>她说，“那不能一年年签了。你住过的别人来住不就要压我价钱了。至少签三年。”</p>

<p>我晕…… “哦，三年啊，我都三十多了，说不准得在您的房子里结婚了”</p>

<p>“啊？对啊。那可不行! 嗯，不过呢，现在有本事的人都四十才结婚的。”</p>

<p>谈到房租的问题，</p>

<p>先说，合同上应该多写点，我很有钱，也不在乎钱，不过房子租金高也有面子。</p>

<p>我说，我这人遵纪守法。你不愿意纳个人所得税就算了，我租你的房子合同上多少钱，我可都去地税局自掏腰包帮你把税交了的。然后我逗她说，你说不缺钱，那么你少收我几个房租，我们把合同金额写高点。反正我还是掏这么多钱，无非少给点您，多给点国家。然后这房东利马不干了，急忙回到：那还是多点钱好，钱总是好用的。</p>

<p>谈到税的问题了：我一本正经的说，这我可得给您上上课，纳税是我们公民的义务。她一听连连说是。
接下来说，但是还是不交的好，你这一交了，在地税备了案。下面的房客不肯交了，查下来，我多大的损失啊。</p>

<p>最后，我实在受不了跟一个很有素质，但就是无法用我的逻辑体系去沟通的有钱人沟通。说，要么把，我们去打民事官司吧。当然了，这挺麻烦，不过我会花大价钱请个大律师朋友全权帮我处理这事。这样至少我个人不麻烦了。不就是花点钱么，您也不在乎钱，法律是公正的。</p>

<p>她一听就不愿意了，说，好吧，那我今天就不走了。你今天晚上搬走，算我违约（租房合同）法院说啥那就是啥了。</p>

<hr />

<p>好吧，我承认很心里真的在笑她了。也为自己纠缠在这点小事上耽误时间不值。</p>

<p>虽然我已经打算她要什么就给什么了，但还是忍不住逗逗。给了个意见：</p>

<p>不如我们把地板都拆下来看看，请第三方机构严格鉴定。如果有一切损害，我双倍赔偿（其实我认定这房东的地板一点损失都没有）；不过呢，如果鉴定结果说没问题呢，请您赔偿我一点点精神损失费。</p>

<p>我觉得这中年妇女都快跳起来了，说，那怎么行。就算鉴定，损伤了赔双倍没问题，但是即使没问题，这拆下来了再还原，我这也蒙受损失了，也得赔。（寒，感情我证明自己没错了，也得赔偿您呐）好吧，说了几句，她还是没忘记补上一句，其实我不在乎钱的，我很好说话的。</p>

<hr />

<p>还有好多好笑的，不写了。最后我认栽。最后按她的意思，赔偿一万，并续租一年（五万租金）。</p>

<p>写字条的时候，把情况写明，说这事就算了解了。她犹豫着不想签字，说，这样写不好吧。说不再追究地板的事情，你要明年把我地板（故意）刮花了，我怎么办啊。我的地板很贵的…… 真的很无语。</p>

<p>好不容易把她老人家送出门。没过两分钟就上来敲门：明天你会把钱打到我帐上的吧？我可都签字了的。只好提醒她，似乎你收我的钱都不开收条的哦。</p>

<hr />

<p>现在的许多人素质的确是高多了。不像上次楼下那家，差不多快冲上来纠住我说，你丫的要不赔钱，我找黑社会的人废了你（原话是：我认识很多人的，一个电话能叫 30 个来）。好吧，上次我没笑出来，这次我真的笑了。</p>
]]>
    </content>
</entry>
<entry>
    <title>树型打印一个 table</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/05/print_r.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=464" title="树型打印一个 table" />
    <id>tag:blog.codingnow.com,2009://1.464</id>
    
    <published>2009-05-03T06:38:06Z</published>
    <updated>2009-05-03T06:48:02Z</updated>
    
    <summary>php 中有个 print_r 函数，可以递归打印一张表。很多 php 程序员喜欢用这个去调试程序。 我想，所有写过一定代码量的 lua 程序员都会写一个类似的东西放着备用吧。这两天调试 lua 程序的时候，发现以前做的简陋的 print_r 不够好用。对于复杂的 table 打印出来一大篇很不直观。结果就放下手头的工作，花了整整一个小时，写了下面几十行代码。把 table 输出成树结构。 比如： a = {} a.a = { hello = { alpha = 1 , beta = 2, }, world = { foo =...</summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="lua与虚拟机" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p>php 中有个 <code>print_r</code> 函数，可以递归打印一张表。很多 php 程序员喜欢用这个去调试程序。</p>

<p>我想，所有写过一定代码量的 lua 程序员都会写一个类似的东西放着备用吧。这两天调试 lua 程序的时候，发现以前做的简陋的 <code>print_r</code> 不够好用。对于复杂的 table 打印出来一大篇很不直观。结果就放下手头的工作，花了整整一个小时，写了下面几十行代码。把 table 输出成树结构。</p>

<p>比如：</p>

<pre>
a = {}

a.a = { 
    hello = { 
        alpha = 1 ,
        beta = 2,
    },
    world =  {
        foo = "ooxx",
        bar = "haha",
        root = a,
    },
}
a.b = { 
    test = a.a 
}
a.c = a.a.hello

print_r(a)
</pre>

<p>可以输出成：</p>

<pre>
+a+hello+alpha [1]
| |     +beta [2]
| +world+root {.}
|       +bar [haha]
|       +foo [ooxx]
+c {.a.hello}
+b+test {.a}
</pre>
]]>
        <![CDATA[<p>代码参考这里：</p>

<p><a href="http://blog.codingnow.com/cloud/LuaPrintR">树型打印一个 table</a></p>

<p>其实实现的细节需要注意的就两个地方：一个是考虑循环引用的问题。（一般用 table 模拟树结构都会记录一个 parent 节点的引用，这样就造成了循环引用）</p>

<p>一个是表格线的处理，对最后一个子节点需要特殊处理。</p>

<p>这段代码的输出风格不算美观，至少不如那些显示文件目录结构的命令行工具。不过我觉得是够用就成，另外需要尽量紧凑，不浪费空间。需要更美观的同学，其实也可以很容易的对其改进。</p>
]]>
    </content>
</entry>
<entry>
    <title>在 lua 中实现函数的重载</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/05/lua_function_overload.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://blog.codingnow.com/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=463" title="在 lua 中实现函数的重载" />
    <id>tag:blog.codingnow.com,2009://1.463</id>
    
    <published>2009-05-01T09:17:08Z</published>
    <updated>2009-05-01T10:44:45Z</updated>
    
    <summary>警告：记录以下内容纯粹自娱，请勿轻易用于项目。我个人也不赞同随意使用语法糖去改造语言。 我们知道 C++ 里有函数重载的特性，程序员可以为一个看起来同名的函数做多份实现，让编译器通过调用时的参数类型去指定链接器链接最为匹配的一份实现。对于死忠的 C++ 程序员，这绝对是最必不可少的利器。如果没有它，那些 template 绝对玩不出现在这么多花来，当然也就没那么多机会拿着“充满智慧” 花哨的 template 代码来 YY 自己的智商了。 哦，写 lua 的所谓脚本程序员不要沮丧，其实 lua 中可玩的花样也很多。一样可以写出让同行瞠目结舌的代码来。比如这个函数重载的问题，虽然 lua 不可能做所谓编译期运算（动态生成代码或许勉强算一个），也没有什么静态链接过程。 但 lua 是个有趣的语言，下面看我怎么模拟出一个类似的东西来。...</summary>
    <author>
        <name>云风</name>
        <uri>http://www.codingnow.com</uri>
    </author>
            <category term="lua与虚拟机" />
    
    <content type="html" xml:lang="en" xml:base="http://blog.codingnow.com/">
        <![CDATA[<p>警告：记录以下内容纯粹自娱，请勿轻易用于项目。我个人也不赞同随意使用语法糖去改造语言。</p>

<p>我们知道 C++ 里有函数重载的特性，程序员可以为一个看起来同名的函数做多份实现，让编译器通过调用时的参数类型去指定链接器链接最为匹配的一份实现。对于死忠的 C++ 程序员，这绝对是最必不可少的利器。如果没有它，那些 template 绝对玩不出现在这么多花来，当然也就没那么多机会拿着“充满智慧” 花哨的 template 代码来 YY 自己的智商了。</p>

<p>哦，写 lua 的所谓脚本程序员不要沮丧，其实 lua 中可玩的花样也很多。一样可以写出让同行瞠目结舌的代码来。比如这个函数重载的问题，虽然 lua 不可能做所谓编译期运算（<a href="http://blog.codingnow.com/2008/08/compile_time_calculation_in_lua.html">动态生成代码</a>或许勉强算一个），也没有什么静态链接过程。</p>

<p>但 lua 是个有趣的语言，下面看我怎么模拟出一个类似的东西来。</p>
]]>
        <![CDATA[<p>我们的目标大约是这样的：</p>

<pre>
    define.test {
        "number",
        function(n)
            print("number",n)
        end
    }

    define.test {
        "string",
        "number",
        function(s,n)
            print("string number",s,n)
        end
    }

    define.test {
        "number",
        "...",
        function(n,...)
            print("number ...",n,...)
        end
    }

    define.test {
        "...",
        function(...)
            print("default",...)
        end
    }
</pre>

<p>然后调用 test 的时候，可以通过调用参数，分发到不同的函数实现上去。当然，需要找到最接近的匹配。</p>

<pre>

test(1)
test("hello",2)
test("hello","world")
test(1,"hello")

将输出

number  1
string number   hello   2
default hello   world
number ...      1       hello

</pre>

<p>这件事情其实很容易做，只需要额外为每个需要用多态函数的环境记录一张表。里面放上同名函数的分发器。而分发器按事先注册的参数表去检查匹配参数，找到最合适的对应函数定义。</p>

<p>如果得不到任何匹配就抛出异常，这样也可以在需要强类型检查时增强一些安全性。</p>

<p>不过这些事情当然会有性能上的开销，也就是代价吧。既然是玩具代码，我也没太优化，纯粹是过节自娱。</p>

<p>大家节日快乐。</p>

<p>附上代码：
<a href="http://blog.codingnow.com/cloud/LuaFunctionOverload">LuaFunctionOverload</a></p>

<p>ps. 如果在定义函数的地方，定义参数默认值也很容易，这里就不给出代码了。</p>
]]>
    </content>
</entry>

</feed> 

