<?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,2010://1</id>
    <link rel="service.post" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1" title="云风的 BLOG" />
    <updated>2010-02-07T17:40:42Z</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/2010/02/oeouo.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=541" title="关于招聘" />
    <id>tag:blog.codingnow.com,2010://1.541</id>
    
    <published>2010-02-07T17:31:00Z</published>
    <updated>2010-02-07T17:40:42Z</updated>
    
    <summary>承蒙大家厚爱，到现在，我已经通过 email 收到了 21 份简历。不过两天时间就能收到这么多，而且看得出大家都是很热情，大多数不仅仅附了简历，还专门写了好多文字。还真是受宠若惊啊。 很不好意思，我只回复了两份，别的都还没有动键盘回复。一是周末在打游戏（平时实在没空），二是需要和同事讨论一下才有结论。我们工作室 11 号放假，10 号晚上办公室就没人了。也就这两天会比较所有的简历吧。 嗯，已收到的简历我都看完了（真是花了不少时间呢），明天会发给工作室其他写程序的同事看。我想若安排面试，就是年后的事情了。我们大约在 21 号正式上班。 请原谅我这个周末的懈怠。接下来我会尽力回复 email 的。...</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>承蒙大家厚爱，到现在，我已经通过 email 收到了 21 份简历。不过两天时间就能收到这么多，而且看得出大家都是很热情，大多数不仅仅附了简历，还专门写了好多文字。还真是受宠若惊啊。</p>

<p>很不好意思，我只回复了两份，别的都还没有动键盘回复。一是周末在打游戏（平时实在没空），二是需要和同事讨论一下才有结论。我们工作室 11 号放假，10 号晚上办公室就没人了。也就这两天会比较所有的简历吧。</p>

<p>嗯，已收到的简历我都看完了（真是花了不少时间呢），明天会发给工作室其他写程序的同事看。我想若安排面试，就是年后的事情了。我们大约在 21 号正式上班。</p>

<p>请原谅我这个周末的懈怠。接下来我会尽力回复 email 的。</p>
]]>
        

    </content>
</entry>
<entry>
    <title>FF13 剧情完成</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2010/02/ff13.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=540" title="FF13 剧情完成" />
    <id>tag:blog.codingnow.com,2010://1.540</id>
    
    <published>2010-02-07T16:38:59Z</published>
    <updated>2010-02-07T17:08:06Z</updated>
    
    <summary>终于盼到周末，把 FF13 熬夜完成了。 游戏时间大约在 50 小时左右，没看任何攻略，没有炼级，没有改武器，基本一路冲到底的。就是几个 boss 稍微难一点，retry 了几次，大体上对于熟练 RPG 玩家没什么难度。 然后接着就是回头做 64 个冥碑任务（现在完成了 20 多个，其中只做了一个 A 级的比较难），开传送点等等。我估计把盘走完，还需要至少五十小时吧。 这次剧情依旧不错，我比较喜欢，但不是最好。（FF 10 和 FF 7 我更喜欢一点）我的日文水平是入门级里的入门，理所当然的，好多地方没看明白。只能连蒙带猜了。生日那天，居然传出官方消息说是五月要出中文版，这算是今年的生日礼物吧 :D 一定会买来再玩一次的。 角色里三个 mm 都不错，任挑一个我都喜欢。不过感觉这次有点恶趣味。虽然没明白做百合向的，我想有几个镜头也是故意做的满邪恶了 :D...</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>终于盼到周末，把 FF13 熬夜完成了。</p>

<p>游戏时间大约在 50 小时左右，没看任何攻略，没有炼级，没有改武器，基本一路冲到底的。就是几个 boss 稍微难一点，retry 了几次，大体上对于熟练 RPG 玩家没什么难度。</p>

<p>然后接着就是回头做 64 个冥碑任务（现在完成了 20 多个，其中只做了一个 A 级的比较难），开传送点等等。我估计把盘走完，还需要至少五十小时吧。</p>

<p>这次剧情依旧不错，我比较喜欢，但不是最好。（FF 10 和 FF 7 我更喜欢一点）我的日文水平是入门级里的入门，理所当然的，好多地方没看明白。只能连蒙带猜了。生日那天，居然传出官方消息说是五月要出中文版，这算是今年的生日礼物吧 :D 一定会买来再玩一次的。</p>

<p>角色里三个 mm 都不错，任挑一个我都喜欢。不过感觉这次有点恶趣味。虽然没明白做百合向的，我想有几个镜头也是故意做的满邪恶了 :D</p>
]]>
        <![CDATA[<p>战斗系统的进化我很欣赏。敢于放弃传统，把战斗的爽快感做出来而不失简洁和策略。强化 Break 系统真是让人拍案。虽然从里面可以看到 FFX / XI / XII / X-2 / VII CC 等等的影子，但放在一起再加一点点就是个全新的东西。融合的很好。</p>

<p>人物基本的可升级数值设定只留下 HP 物理 魔法 三个值（加上 ATB 槽和职业两个等级），我觉得是要莫大的勇气的。不过想想也够了。即可以做得丰富，又不多余。</p>

<p>我想 ATB 进化到这个样子，我们已经不能单纯把 RPG 游戏分成即时和回合制了。也并非如许多策划念叨的：回合的间隔缩小就成了即时。FF13 每次做出操作选择的时间间隔并不短，但流畅度，紧张感，爽快感却并不见少。</p>

<p>其实 FF13 里吸收的元素都不是新鲜东西，只是很融洽的组合在一起罢了。</p>
]]>
    </content>
</entry>
<entry>
    <title>招聘程序员</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2010/02/recruit.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=539" title="招聘程序员" />
    <id>tag:blog.codingnow.com,2010://1.539</id>
    
    <published>2010-02-05T08:29:00Z</published>
    <updated>2010-02-05T08:55:49Z</updated>
    
    <summary>今天发个不太正式的招聘信息： 我们这里工作室，需要招聘两名程序员。在未来至少一年的工作是：专职制作维护 3d engine 中的模型编辑器和场景编辑器。（每个一人维护） 要求： 熟悉 C 语言，或 C++ 。至少有一门动态语言的经验，lua 最好，python 或其它也可以。 能够全心制作编辑器（至少一年），最好有相关经验（非必须）。 有 3d 方面编程经验优先，但不是必须。需要有学习 3d 编程的兴趣和动力。 有任意一款 GUI 框架编程经验者优先。（例如 QT ） 待遇方面，不会太高，但也不算低。所以特别牛的同学就免了，可能满足不了你的要求。就算愿意，我也心里不舒服。 完全抱着从头学习的想法的同学也需要考虑一下。我们还是需要有一点点经验的，不可能花半年时间慢慢培养到可以开始动手干活。（经验指：可以独立编写 GUI 程序） 有兴趣的同学可以给我发 email 附上简单的简历。...</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>我们这里工作室，需要招聘两名程序员。在未来至少一年的工作是：专职制作维护 3d engine 中的模型编辑器和场景编辑器。（每个一人维护）</p>

<p>要求：</p>

<ol>
<li><p>熟悉 C 语言，或 C++ 。至少有一门动态语言的经验，lua 最好，python 或其它也可以。</p></li>
<li><p>能够全心制作编辑器（至少一年），最好有相关经验（非必须）。</p></li>
<li><p>有 3d 方面编程经验优先，但不是必须。需要有学习 3d 编程的兴趣和动力。</p></li>
<li><p>有任意一款 GUI 框架编程经验者优先。（例如 QT ）</p></li>
</ol>

<p>待遇方面，不会太高，但也不算低。所以特别牛的同学就免了，可能满足不了你的要求。就算愿意，我也心里不舒服。</p>

<p>完全抱着从头学习的想法的同学也需要考虑一下。我们还是需要有一点点经验的，不可能花半年时间慢慢培养到可以开始动手干活。（经验指：可以独立编写 GUI 程序）</p>

<p>有兴趣的同学可以给我发 email 附上简单的简历。</p>
]]>
        <![CDATA[<p>附：工作地点在杭州。一周五天工作，无强制加班。入职公司是网易（杭州）。</p>

<p>我们是小团队，工作室制。我是团队负责人，全权负责团队一切事务。工作室下属于网易游戏部门（网易互动娱乐）。</p>

<p>团队以研发为主，暂不涉及游戏运营等。</p>
]]>
    </content>
</entry>
<entry>
    <title>古怪的 C++ 问题</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2010/01/cpp_template.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=522" title="古怪的 C++ 问题" />
    <id>tag:blog.codingnow.com,2010://1.522</id>
    
    <published>2010-01-28T15:59:58Z</published>
    <updated>2010-01-28T08:21:57Z</updated>
    
    <summary>我好多年没写 C++ 程序了，读 C++ 代码也是偶尔为之。 今天晚上就碰到这么一个诡异的问题，我觉得是我太久没摸 C++ 了，对那些奇怪的语法细则已经不那么熟悉了。有知道的同学给我解惑一下吧。 事情的起因是，我想安装一个 perl 模块唤作 Syntax::Highlight::Universal 。 本来用 CPAN 安装很方便的，直接 install 即可。 可是在我的机器上，make 死活通不过。我就仔细研究了一下编译出错信息。又读了一下源代码，自己感觉没错。纠结了半天，仔细模仿出错的地方写了一小段程序测试。...</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>我好多年没写 C++ 程序了，读 C++ 代码也是偶尔为之。</p>

<p>今天晚上就碰到这么一个诡异的问题，我觉得是我太久没摸 C++ 了，对那些奇怪的语法细则已经不那么熟悉了。有知道的同学给我解惑一下吧。</p>

<p>事情的起因是，我想安装一个 perl 模块唤作 Syntax::Highlight::Universal 。</p>

<p>本来用 CPAN 安装很方便的，直接 install 即可。</p>

<p>可是在我的机器上，make 死活通不过。我就仔细研究了一下编译出错信息。又读了一下源代码，自己感觉没错。纠结了半天，仔细模仿出错的地方写了一小段程序测试。</p>
]]>
        <![CDATA[<pre class="mtc_block">template <span class="c_Symbol def_Symbol">&lt;</span>class T<span class="c_Symbol def_Symbol">&gt;</span>
class A <span class="def_SymbolStrong def_Symbol"><span class="def_PairStart def_Special">{</span></span>
<span class="c_Label def_Label">protected:</span>
        <span class="c_KeywordANSI_typenames c_KeywordANSI def_Keyword">int</span> a<span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span>
<span class="def_SymbolStrong def_Symbol"><span class="def_PairEnd def_Special">}</span></span><span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span>

template <span class="c_Symbol def_Symbol">&lt;</span>class T<span class="c_Symbol def_Symbol">&gt;</span>
class B <span class="c_StructureSymbol def_SymbolStrong def_Symbol">:</span> public A<span class="c_Symbol def_Symbol">&lt;</span>T<span class="c_Symbol def_Symbol">&gt;</span> <span class="def_SymbolStrong def_Symbol"><span class="def_PairStart def_Special">{</span></span>
<span class="c_Label def_Label">public:</span>
        <span class="c_KeywordANSI_typenames c_KeywordANSI def_Keyword">void</span> <span class="c_FuncOutline def_Outlined def_Special">foo</span><span class="c_Symbol def_Symbol"><span class="def_PairStart def_Special">(</span></span><span class="c_Symbol def_Symbol"><span class="def_PairEnd def_Special">)</span></span> <span class="def_SymbolStrong def_Symbol"><span class="def_PairStart def_Special">{</span></span> 
            a<span class="c_Symbol def_Symbol">=</span><span class="def_NumberDec def_Number">0</span><span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span>
        <span class="def_SymbolStrong def_Symbol"><span class="def_PairEnd def_Special">}</span></span>
<span class="def_SymbolStrong def_Symbol"><span class="def_PairEnd def_Special">}</span></span><span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span></pre>

<p>各位同学觉得有问题么？我初看觉得没有。但是 gcc 一编译就出错。一开始是觉得 gcc 版本太高（4.x），可能语法检查更严格了。后来换了 gcc3 ，问题依旧。</p>

<p>出错信息如下：</p>

<p>a.cpp: In member function `void B<T>::foo()':</p>

<p>a.cpp:11: error: `a' was not declared in this scope</p>

<p>就是在 B 里找不到 A 定义的成员变量 a 。</p>

<p>如果 A 不是一个 template ，那么这个问题就没有。</p>

<p>我琢磨着这个问题跟编译器为 template 生成代码的行为有关，但是不确定。</p>

<p>注：这段代码在 VC6 里是可以正常编译的。</p>

<p>最后，我试了一下，把代码改成</p>

<pre><code>    void foo() { 
        this-&gt;a=0;
    }
</code></pre>

<p>那么是可以编译通过的了。可是，这是新标准规定的么？</p>

<p>btw, 其实我写 C++ 的最后一年，都养成了显式用 this 指针的习惯。这样比较少犯错误。</p>
]]>
    </content>
</entry>
<entry>
    <title>最终幻想XIII</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2010/01/ff13.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=521" title="最终幻想XIII" />
    <id>tag:blog.codingnow.com,2010://1.521</id>
    
    <published>2010-01-27T12:26:39Z</published>
    <updated>2010-01-27T04:48:10Z</updated>
    
    <summary>周六部门组织去滑雪，我第一次，还不错。回来办公室发现同事桌子上有本杂志，封面是最终幻想XIII 。顺手拿回家躺床上看。 翻了几页杂志，回想到当年最终幻想X 出的时候的惊艳。当时看到公司里有同事在玩，也是周末。第二天去去买了台 PS2 和游戏。之后，我就成了这个系列的死忠（之前我也玩过七和八，但是都没有十给我的震撼）。后来我补上了五和六，并且把七和八重玩了；再之后就是 XI 和 XII ，以及 X 和 VII 的外传。 很久没玩游戏了，想想真是忙呢。又或者是长大了？但是，我得承认，我心里那点感觉又回来了。星期天一起床，吃完饭就直接去了附近的游戏店。价钱很黑，但是我急着想要。不到五分钟就拿下了 FF13 连 PS3 的套装。...</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>周六部门组织去滑雪，我第一次，还不错。回来办公室发现同事桌子上有本杂志，封面是最终幻想XIII 。顺手拿回家躺床上看。</p>

<p>翻了几页杂志，回想到当年最终幻想X 出的时候的惊艳。当时看到公司里有同事在玩，也是周末。第二天去去买了台 PS2 和游戏。之后，我就成了这个系列的死忠（之前我也玩过七和八，但是都没有十给我的震撼）。后来我补上了五和六，并且把七和八重玩了；再之后就是 XI 和 XII ，以及 X 和 VII 的外传。</p>

<p>很久没玩游戏了，想想真是忙呢。又或者是长大了？但是，我得承认，我心里那点感觉又回来了。星期天一起床，吃完饭就直接去了附近的游戏店。价钱很黑，但是我急着想要。不到五分钟就拿下了 FF13 连 PS3 的套装。</p>
]]>
        <![CDATA[<p>毕竟没让我失望啊。</p>

<p>我想这一作可以超过 X 在我心中的地位了。</p>

<p>不过一开始没有买 HDMI 线，文字相当模糊。昨天又在网上订了跟线，今天到了。换成高清模式舒服多了。看来这线是玩 FF13 必备的。</p>

<p>这次的战斗系统做的非常简洁明快。爽快感十足。还解答了我前年的一个疑问：</p>

<p>之前我做个一个战斗设定：回复 HP 技能可以来得比较轻易，并且几乎无消耗。强调爽快感，不要有太多繁杂操作（比如磕药，复杂的加血技能）干扰战斗。但是沙盘了半天都觉得很难做平衡，没有想出和高回复力对应的设定应该是什么。</p>

<p>FF13 告诉我，可以有一个 Break 系统做一个对应的补充。玩家重点在于控制操作的次序即可。（当然还有职业更换的策略）</p>

<p>之前我的构思里有个类似的设定，但是思维产生了个死角，没有把系统做成非线性的。而用了一个高斯分布。其实加一个 Break 临界点，就好玩了许多。</p>

<p>这些都是闲话了，等对此作的战斗系统有了更深刻理解后，再总结一下。</p>

<p>看网上介绍，这次的剧情通关应该在 40 小时左右，我现在游戏时间才 20 小时。争取这周先一周目吧。</p>
]]>
    </content>
</entry>
<entry>
    <title>招行虽然烂，但至少可以用</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2010/01/bank.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=520" title="招行虽然烂，但至少可以用" />
    <id>tag:blog.codingnow.com,2010://1.520</id>
    
    <published>2010-01-21T07:30:45Z</published>
    <updated>2010-01-21T06:26:36Z</updated>
    
    <summary>招行的网银只支持 Windows ，这点不只一个人像我这样痛恨过了 。我想大家都是恨铁不成钢吧。 别的银行我也用过几家，也骂过。比如农行早几年取款难。每个季度我都去转工资，每次要排 3 小时的队。后来花了 100 办了网上银行，居然跨行转帐最长等了我一周。后来工资转发到招行，总算解决了问题。愤然去消户，跑了两趟银行，交通费花了几十，手续费花了十块（补交年费，还有把卡弄坏的挂失费等等）。...</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 ，<a href="http://blog.codingnow.com/2007/02/cmb.html">这点不只一个人像我这样痛恨过了</a>  。我想大家都是恨铁不成钢吧。</p>

<p>别的银行我也用过几家，也骂过。比如农行早几年取款难。每个季度我都去转工资，每次要排 3 小时的队。后来花了 100 办了网上银行，居然跨行转帐最长等了我一周。后来工资转发到招行，总算解决了问题。愤然去消户，跑了两趟银行，交通费花了几十，手续费花了十块（补交年费，还有把卡弄坏的挂失费等等）。</p>
]]>
        <![CDATA[<p>当年信用卡还不普及的时候，招行的信用卡在一些酒店押不了房费。我琢磨着就想办张中国银行的信用卡。当时我已经在公司楼下的中国银行开过张借记卡，用来交电话费。里面常年有几万的余额。为了办那张信用卡，我填了无数资料。复印了很多材料。包括工资单，招行信用卡的还款单。买的房子的房契。等等。足够证明我是个信用良好，收入稳定的好市民了。</p>

<p>中国银行的人再给我打了两个电话，并陆续索取了一些我的个人材料后，通知我，审核没通过。不给办信用卡……  看着满大街送上门的卡（我抽屉里还有张没开的广发信用卡），我彻底无语了。看来跟谈恋爱一样，只要是你主动找上门去的，立马掉价了。</p>

<p>此外，我还用过邮政储蓄，在读大学时办过张交通银行的卡（当时唯一在校园里有网点的银行）。读书时有次邮政储蓄内部系统错误，导致有些取款机异地取了不钱。我当时身无分文，（没钱买公交车票）在长沙市区里步行了 4 个多小时找可以取款的柜台。</p>

<p>最近因为需要，在建设银行以及交通银行都办了些业务。</p>

<p>早几年建设银行硬件水平之滥我早有耳闻。前几年，我作保，给朋友贷了一笔钱。资金打在了北京的建设银行里。这个朋友想从杭州把钱取出来，被告之，建设银行内部南北不通，是两个系统。这么转帐要收四位数的手续费。当时我就惊诧了。如果不考虑时间和精力，还不如自己北上去柜台办呢。</p>

<p>这些年应该好了些。可今天去建设银行转帐。柜台上手续繁杂不说。同城跨行居然要收 25 。还是打了 5 折的。我在招行免费转帐习惯了，还真有点不适应。（招行即使没有金葵花卡，同城跨行也只要 2 元）末了，银行的人推荐我开通手机银行，说是汇款 3 折。头三个月免费。</p>

<p>注意，是头三个月，以后每个月还是要收 6 元的。至于网上银行，必须买个 USB key 。</p>

<p>然后是交通银行，我免费开通的手机密码登陆网上银行。毫不怀疑的打开了 IE ，我想 Chrome Firefox 铁定是支持不了的了。</p>

<p>结果，弹出个 javascript 出错框，唉，习惯了，见怪不怪。安装了所谓安全输入控件后。我登陆了两次硬是没进去。不是我密码错误，而是那个图片校验码错…… 然后，我的帐户被锁定了。告之，去银行柜台解锁吧。</p>

<p>嗯，在其它银行软硬件不跟进的情况下，我就不指望招行能出跨平台的网银了。谁叫咱别无选择呢。</p>
]]>
    </content>
</entry>
<entry>
    <title>浅谈 C 语言中模块化设计的范式</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2010/01/modularization_in_c_1.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=519" title="浅谈 C 语言中模块化设计的范式" />
    <id>tag:blog.codingnow.com,2010://1.519</id>
    
    <published>2010-01-20T17:58:31Z</published>
    <updated>2010-01-20T10:10:10Z</updated>
    
    <summary>今天继续谈模块化的问题。这个想慢慢写成个系列，但是不一定连续写。基本是想起来了，就整理点思路出来。主要还是为以后集中整理做点铺垫。 我们都知道，层次分明的代码最容易维护。你可以轻易的换掉某个层次上的某个模块，而不用担心对整个系统造成很大的副作用。 层次不清的设计中，最糟糕的一种是模块循环依赖。即，分不清两个模块谁在上，谁在下。这个时候，最容易牵扯不清，其结果往往是把两者看做一体去维护算了。这里面还涉及一些初始化次序等繁杂的细节。 其次，就是越层的模块联系。当模块 A 是模块 B 的上层，而模块 B 又是模块 C 的上层，这个时候，让模块 C 对模块 A 可见，在模块 A 中有对 C 导出接口的直接调用，对于清晰的设计是很忌讳的一件事。虽然，我们很难完全避免这个问题，去让 A 对 C 的调用完全通过 B 。但通常应尽力为之。（注：以后写书的话，我争取补充一些实际的例子来说明）不过，对语言不原生支持的数据类型，以及基础设施，但却有必要创造出来给系统用的。可以有些例外。比如内存管理，log 管理，字符串（C 语言用原始库函数管理比较麻烦）等等，我们可能以基础模块的形式提供。但却可能被不同层次的模块直接使用。但，上到一定层次后，还是需要去隐藏它们的。 下面来一点更实际的分析。...</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>层次不清的设计中，最糟糕的一种是模块循环依赖。即，分不清两个模块谁在上，谁在下。这个时候，最容易牵扯不清，其结果往往是把两者看做一体去维护算了。这里面还涉及一些初始化次序等繁杂的细节。</p>

<p>其次，就是越层的模块联系。当模块 A 是模块 B 的上层，而模块 B 又是模块 C 的上层，这个时候，让模块 C 对模块 A 可见，在模块 A 中有对 C 导出接口的直接调用，对于清晰的设计是很忌讳的一件事。虽然，我们很难完全避免这个问题，去让 A 对 C 的调用完全通过 B 。但通常应尽力为之。（注：以后写书的话，我争取补充一些实际的例子来说明）不过，对语言不原生支持的数据类型，以及基础设施，但却有必要创造出来给系统用的。可以有些例外。比如内存管理，log 管理，字符串（C 语言用原始库函数管理比较麻烦）等等，我们可能以基础模块的形式提供。但却可能被不同层次的模块直接使用。但，上到一定层次后，还是需要去隐藏它们的。</p>

<p>下面来一点更实际的分析。</p>
]]>
        <![CDATA[<p>以 C 语言为例，由于 C 语言缺乏 namespace 的原生支持，我们通常给 api 加上统一前缀来区分。这倒也不麻烦。</p>

<p>那么模块 A 看起来就是一堆 'A_xxxxx' 为名字的方法。我个人主张单个模块不宜过大，在实现时适合放在同一个 .c 文件里即可。通常，一个模块会围绕一类对象处理。这些对象可以用整数 handle 来表示，也可以用一个特定类型的对象指针。两种方案各有千秋。先来谈对象指针的方案。</p>

<p>一个模块 A 的接口描述文件很可以是这样的（希望以后能补上更现实的代码）:</p>

<pre class="mtc_block"><span class="c_Preproc def_Directive">#<span class="c_PreprocWord def_DirectiveContent def_Directive">ifndef</span> _A_h</span>
<span class="c_Preproc def_Directive">#<span class="c_PreprocWord def_DirectiveContent def_Directive">define</span> <span class="c_DefineOutline def_Outlined def_Special">_A_h</span></span>

<span class="c_KeywordANSI def_Keyword">struct</span> A<span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span>
<span class="c_KeywordANSI def_Keyword">struct</span> B<span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span>

<span class="c_KeywordANSI def_Keyword">struct</span> A<span class="c_Symbol def_Symbol">*</span> A_create<span class="c_Symbol def_Symbol"><span class="def_PairStart def_Special">(</span></span><span class="c_KeywordANSI_typenames c_KeywordANSI def_Keyword">void</span><span class="c_Symbol def_Symbol"><span class="def_PairEnd def_Special">)</span></span><span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span>
<span class="c_KeywordANSI_typenames c_KeywordANSI def_Keyword">void</span> A_release<span class="c_Symbol def_Symbol"><span class="def_PairStart def_Special">(</span></span><span class="c_KeywordANSI def_Keyword">struct</span> A <span class="c_Symbol def_Symbol">*</span>self<span class="c_Symbol def_Symbol"><span class="def_PairEnd def_Special">)</span></span><span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span>
<span class="c_KeywordANSI_typenames c_KeywordANSI def_Keyword">void</span> A_bind<span class="c_Symbol def_Symbol"><span class="def_PairStart def_Special">(</span></span><span class="c_KeywordANSI def_Keyword">struct</span> A <span class="c_Symbol def_Symbol">*</span>self <span class="c_Symbol def_Symbol">,</span> <span class="c_KeywordANSI def_Keyword">struct</span> B <span class="c_Symbol def_Symbol">*</span>b<span class="c_Symbol def_Symbol"><span class="def_PairEnd def_Special">)</span></span><span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span>
<span class="c_KeywordANSI_typenames c_KeywordANSI def_Keyword">void</span> A_commit<span class="c_Symbol def_Symbol"><span class="def_PairStart def_Special">(</span></span><span class="c_KeywordANSI def_Keyword">struct</span> A <span class="c_Symbol def_Symbol">*</span>self<span class="c_Symbol def_Symbol"><span class="def_PairEnd def_Special">)</span></span><span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span>
<span class="c_KeywordANSI_typenames c_KeywordANSI def_Keyword">void</span> A_update<span class="c_Symbol def_Symbol"><span class="def_PairStart def_Special">(</span></span><span class="c_KeywordANSI_typenames c_KeywordANSI def_Keyword">void</span><span class="c_Symbol def_Symbol"><span class="def_PairEnd def_Special">)</span></span><span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span>

<span class="c_KeywordANSI_typenames c_KeywordANSI def_Keyword">int</span> A_init<span class="c_Symbol def_Symbol"><span class="def_PairStart def_Special">(</span></span><span class="c_KeywordANSI_typenames c_KeywordANSI def_Keyword">void</span><span class="c_Symbol def_Symbol"><span class="def_PairEnd def_Special">)</span></span><span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span>

<span class="c_Preproc def_Directive">#<span class="c_PreprocWord def_DirectiveContent def_Directive">endif</span></span></pre>

<p>这里，我们定义了 A 这种数据类型。我个人反对用 typedef 或宏来减少代码输入。除非有特别的理由，都写上 struct 前缀，而不是定义出新类型。尤其是在较底层的模块设计时更是如此。在接口描述时，struct A 的细节是绝对不应该暴露出来的，它的数据结构应该仅存在于实现的文件 a.c 中。</p>

<p>关于 A 的接口通常分两类，一类是对 struct A* 做一些处理的，那么就让第一个参数传入 self 指针。这相当于 C++ 的 this 指针。比如上例中的 <code>A_commit</code> ；另一类接近于 C++ 类的静态成员函数，通常用于对这一类对象全部做一个处理，如 <code>A_update</code> 。</p>

<p>注：我无意用 C 去模拟 C++ ，但基于一类数据类型做一些处理的方法，对于 C ，这样的写法也是一个常规的范式而已。至于面向对象等在构建复杂系统时常用到的方法，以后我会谈谈我自己常用的另一些范式。或许像 C++ ，也可以不像。怎么写更好，是个见任见智的问题。不用过于拘泥。</p>

<p>这里的例子中，我们还提到了另一个数据类型 B 。显然，它是放在 B 模块中的。</p>

<p>我们通常不会在 a.h 中去 include b.h ，而只是声明一下 struct B 。（对于 C 语言来说，这并不必要，但写上是个好习惯）。这是因为，如果 B 是位于 A 之下的模块，既在 A 模块的实现中，会用到 B 的方法，我们通常不会让用到 A 模块的人，可以看见 B 的接口。包含 a.h 的同时隐式包含 b.h 就是不必要的了。</p>

<p>从范例代码中，我们可以猜想，struct A 是对 struct B 的某种封装，可以通过对 A 的操作，间接操作到其中的 B 类型。在 A 的模块初始化 <code>A_init</code> 中一定就会初始化 B 了。如果是这样，B 的层次就位于 A 之下。</p>

<p>往往 struct B 中还会保留一个 struct A 类型的引用。首先，我们应该尽力避免这种情况。即：位于下层的 B 应该对上层的 A 一无所知是最好的。如果在 B 模块中必须出现 struct A，那么我们应该至少保证，仅仅是 struct A * ，一个引用，而绝对不能出现任何对 A 模块内接口的调用。不要认为使用巧妙的方法，绕过循环依赖初始化问题就够了。这应该是一个设计原则，不要去违反。 </p>

<p>btw, 草率的接口设计往往是日后系统脆弱的根源。图一时之快，随意暴露一些接口，或是自以为聪明的用一些“巧妙”的方法，甚至是语法糖来绕过设计原则，都是很危险的。</p>

<p>一个常见的难处理的问题是：如果 struct A 和 struct B 相互有双向引用。怎样建立这个引用关系？这个建立的过程，到底是 A 的方法，还是 B 的方法？我的答案是，谁在上层，就是谁的方法。</p>

<p>但是 A 和 B 相互都看不见内部数据布局的细节，让 B 的内部对 A 类型做一个引用，比如也需要从 B 模块中暴露一个接口出来。这个接口，可能仅供 A 使用。在这个例子里，就是仅供 <code>A_bind</code> 这个方法去使用。</p>

<p>如果是 C++ ，我们或许会采用 friend 。也可能使用其它一些技巧。反正 C++ 里可以挖掘的语法太多了。但 C 怎么办？下面给个我自己的方案。</p>

<p>原本，我们在 B 中导出的 api 是这样的：</p>

<pre>
void B_set_A(struct B *self,struct A * a); 
</pre>

<p>现在写成：</p>

<pre>
struct i_A;

void B_set_A(struct B *self,struct i_A *a);
</pre>

<p>在 b.c 的实现中，加一个函数用于 struct i_A * 到 struct A * 的转换。</p>

<pre>
static inline struct A * A(struct i_A *a) { return (struct A *)a; }
</pre>

<p>然后在 a.c 的实现中，加一个类似函数用于转换 struct A * 到 <code>struct i_A *</code> 。</p>

<p>这样，在 a.c 之外，其它模块因为不能得到任何 <code>struct i_A</code> 类型，而不会错误的使用 <code>B_set_A</code> 这个接口了。</p>
]]>
    </content>
</entry>
<entry>
    <title>C 语言对模块化支持的欠缺</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2010/01/c_modularization.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=518" title="C 语言对模块化支持的欠缺" />
    <id>tag:blog.codingnow.com,2010://1.518</id>
    
    <published>2010-01-19T11:00:33Z</published>
    <updated>2010-01-19T04:45:19Z</updated>
    
    <summary>继续昨天的话题。随便列些以后成书可能会写的东西。既然书的主题是：怎样构建一个（稍具规模的）软件。且我选择用 C 为实现工具来做这件事情。就不得不谈语言还没有提供给我们的东西。 模块化是最高原则之一（在 《Unix 编程艺术》一书中， Unix 哲学第一条即：模块原则），我们就当考虑如何简洁明快的使用 C 语言实现模块化。 除开 C/C++ ，在其它现在流行的开发语言中，缺少标准化的模块管理机制是很难想象的。但这也是 C 语言本身的设计哲学决定的：把尽可能多的可能性留给程序员。根据实际的系统，实际的需要去定制自己需要的东西。 对于巨型的系统（比如 Windows 这样的操作系统），一般会考虑使用一种二进制级的模块化方案。由模块自己提供元信息，或是使用统一的管理方案（比如注册表）。稍小一点的系统（我们通常开发接触到的），则会考虑轻量一些的源码级方案。...</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>继续昨天的话题。随便列些以后成书可能会写的东西。既然书的主题是：怎样构建一个（稍具规模的）软件。且我选择用 C 为实现工具来做这件事情。就不得不谈语言还没有提供给我们的东西。</p>

<p>模块化是最高原则之一（在 《Unix 编程艺术》一书中， Unix 哲学第一条即：模块原则），我们就当考虑如何简洁明快的使用 C 语言实现模块化。</p>

<p>除开 C/C++ ，在其它现在流行的开发语言中，缺少标准化的模块管理机制是很难想象的。但这也是 C 语言本身的设计哲学决定的：把尽可能多的可能性留给程序员。根据实际的系统，实际的需要去定制自己需要的东西。</p>

<p>对于巨型的系统（比如 Windows 这样的操作系统），一般会考虑使用一种二进制级的模块化方案。由模块自己提供元信息，或是使用统一的管理方案（比如注册表）。稍小一点的系统（我们通常开发接触到的），则会考虑轻量一些的源码级方案。</p>
]]>
        <![CDATA[<p>首先要考虑的往往是模块的依赖关系和初始化过程。</p>

<p>依赖关系可以放由链接器或加载器来解决。尤其在使用 C 语言时，简单的静态库或动态库，都不太会引起大的麻烦。</p>

<p>C++ 则不然，C++ 的某些特性（比如模板类静态成员的构造）必须对早期只供 C 语言使用的链接器做一些增强。即使是精心编写的 C++ 库，也有可能出现<a href="http://blog.codingnow.com/2009/03/libstdcpp_dlclose_crash.html">一些意外的 bug</a> 。这些 bug 往往需要对编译，链接，加载过程很深刻的理解，才能查出来。注：我并不想以此来反对使用 C++ 做开发。</p>

<p>我们需要着重管理的，是模块的初始化过程。</p>

<p>对于打包在一起的一个库（例如 glibc ，或是 msvcrt ），会在加载时有初始化入口，以及卸载时有结束代码。我想说的不是这个，而是我们自己内部拆分的更小的模块的相互依赖关系。</p>

<p>谁先初始化，谁后初始化，这是一个问题。</p>

<p>在 C++ 的语言级解决方案中，使用的是单件模块。要么由链接器决定以怎样的次序来生成初始化代码，这，通常会因为依赖关系和实际构造次序不同而导致 bug （注：我在某几本 C++ 书中都见过，待核实。自己好久不写 C++ 也没有实际的错误例子）；要么使用惰性初始化方案。这个惰性初始化也不是万能的，并且有些额外的开销。（多线程环境中尤其需要注意）</p>

<p>我使用 C 语言做初期设计的时候，采用的是一种足够简单的方法。就是，以编码规范来规定，每个模块必须存在一个初始化函数，有规范的名字。比如 foo 模块的初始化入口叫 </p>

<p>int foo_init()</p>

<p>规定：凡使用特定模块，必须调用模块初始化函数。</p>

<p>为了避免模块重复初始化，初始化函数并不直接调用，而是间接的。类似这样： <code>mod_using(foo_init);</code></p>

<p><code>mod_using</code> 负责调用初始化函数，并保证不重复调用，也可以检查循环依赖。</p>

<p>在这里，我们还约定了初始化成功于否的返回值。（在我们的系统中，返回 0 表示正确，1 表示失败）然后定义了一个宏来做这个使用。</p>

<p><code>#define USING(m) if (mod_using(m##_init,#m)) { return 1; }</code></p>

<p>注：我个人反对滥用宏。也尽可能的避免它。这里使用宏，经过了慎重的考虑。我希望可以有一个代码扫描器去判断我是否漏掉了模块初始化（可能我使用了一个模块，但忘记初始化它）。宏可以帮助代码扫描分析器更容易实现。而且，使用宏更像是对语言做的轻微且必要的扩展。</p>

<p>这样，我的系统中模块模块的实现代码最后，都有一个 init 函数，里面只是简单的调用了 USING 来引用别的模块。例如：</p>

<pre class="mtc_block"><span class="c_Preproc def_Directive">#<span class="c_PreprocInclude def_Path def_URI">include <span class="c_IncludeOutline def_Outlined def_Special"><span class="c_PreprocIncludeEdge def_StringEdge def_String"><span class="def_PairStart def_Special">&quot;</span></span>module.h<span class="c_PreprocIncludeEdge def_StringEdge def_String"><span class="def_PairEnd def_Special">&quot;</span></span></span></span></span>

<span class="c_Comment def_Comment def_Syntax"><span class="def_PairStart def_Special">/*</span>
  我个人偏爱把 module.h 的引入放在源文件最后，初始化入口之前。
  它里面之定义了 USING 宏，以及相关管理函数。
  这样做是为了避免在代码的其它地方去引入别的模块。
<span class="def_PairEnd def_Special">*/</span></span>

<span class="c_KeywordANSI_typenames c_KeywordANSI def_Keyword">int</span>
<span class="c_FuncOutline def_Outlined def_Special">foo_init</span><span class="c_Symbol def_Symbol"><span class="def_PairStart def_Special">(</span></span><span class="c_Symbol def_Symbol"><span class="def_PairEnd def_Special">)</span></span>
<span class="def_SymbolStrong def_Symbol"><span class="def_PairStart def_Special">{</span></span>
  USING<span class="c_Symbol def_Symbol"><span class="def_PairStart def_Special">(</span></span>memory<span class="c_Symbol def_Symbol"><span class="def_PairEnd def_Special">)</span></span><span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span>  <span class="c_LineComment def_LineComment def_Comment def_Syntax">// 引用内存管理模块</span>
  USING<span class="c_Symbol def_Symbol"><span class="def_PairStart def_Special">(</span></span>log<span class="c_Symbol def_Symbol"><span class="def_PairEnd def_Special">)</span></span><span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span>  <span class="c_LineComment def_LineComment def_Comment def_Syntax">// 引用 log 模块</span>

  <span class="c_KeywordANSI def_Keyword">return</span> <span class="def_NumberDec def_Number">0</span><span class="c_StructureSymbol def_SymbolStrong def_Symbol">;</span>
<span class="def_SymbolStrong def_Symbol"><span class="def_PairEnd def_Special">}</span></span></pre>

<hr />

<p>至于模块的卸载，大部分需求下是不需要的。今天在这里就不论证这一点了。</p>
]]>
    </content>
</entry>
<entry>
    <title>好的设计</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2010/01/good_design.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=516" title="好的设计" />
    <id>tag:blog.codingnow.com,2010://1.516</id>
    
    <published>2010-01-18T10:54:31Z</published>
    <updated>2010-01-18T03:52:16Z</updated>
    
    <summary>前几天说，想再写本书。许多朋友给了我支持。暂时我还写不了。因为： 工作真的很忙，很难脱开身。我觉得我在某种程度上陷进去了。需要花点时间整理规划一下。然后把工作的事情处理好。让它可以顺利做下去。不光是技术上要解决的问题，也不光是管理问题，也不光是团队合作的问题，也不光是项目开发运作的问题。反正很多很多，确实有很多麻烦。我要尽力做好。 觉得上一本没写好，是因为还是太仓促了。即使是已经写的那点东西，也积累不够。写书的经验也太少。我倒不怕有错误被人骂，是怕自己回头不满意。 如果再写，肯定只抓着很少的问题谈。但是具体写什么，还没全想好。积累是有了点，真能够拿出来写写的不多。毕竟，写书和写 blog 瞎扯还是不一样的。 孟岩建议我先在 blog 上列的大纲，然后随便写点。让同学们给意见，再逐步修改成书。我也有此想法，觉得不错。不过一开始，恐怕我连大纲都列不出来，就想到哪写到哪，随便写点东西吧。过段时间再把零碎想法串起来，作为正式列提纲的参考。 由于最近几年用的主要开发语言是 C 和 lua 。那么也打算以此为基础写。假定读者至少有不错的 C 语言基础了。我真正想谈的是，如何把一个软件很好的构建起来。到底需要做些什么。（从实现层面看）怎样才是好的软件。 那么有一个重点问题，也是老问题，怎样才是好的设计。...</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://blog.codingnow.com/2010/01/book.html">想再写本书</a>。许多朋友给了我支持。暂时我还写不了。因为：</p>

<ol>
<li><p>工作真的很忙，很难脱开身。我觉得我在某种程度上陷进去了。需要花点时间整理规划一下。然后把工作的事情处理好。让它可以顺利做下去。不光是技术上要解决的问题，也不光是管理问题，也不光是团队合作的问题，也不光是项目开发运作的问题。反正很多很多，确实有很多麻烦。我要尽力做好。</p></li>
<li><p>觉得上一本没写好，是因为还是太仓促了。即使是已经写的那点东西，也积累不够。写书的经验也太少。我倒不怕有错误被人骂，是怕自己回头不满意。</p></li>
<li><p>如果再写，肯定只抓着很少的问题谈。但是具体写什么，还没全想好。积累是有了点，真能够拿出来写写的不多。毕竟，写书和写 blog 瞎扯还是不一样的。</p></li>
</ol>

<p>孟岩建议我先在 blog 上列的大纲，然后随便写点。让同学们给意见，再逐步修改成书。我也有此想法，觉得不错。不过一开始，恐怕我连大纲都列不出来，就想到哪写到哪，随便写点东西吧。过段时间再把零碎想法串起来，作为正式列提纲的参考。</p>

<p>由于最近几年用的主要开发语言是 C 和 lua 。那么也打算以此为基础写。假定读者至少有不错的 C 语言基础了。我真正想谈的是，如何把一个软件很好的构建起来。到底需要做些什么。（从实现层面看）怎样才是好的软件。</p>

<p>那么有一个重点问题，也是老问题，怎样才是好的设计。</p>
]]>
        <![CDATA[<p>好的设计，必然是容易实现的。它可以很精巧，但不能难以理解。</p>

<p>太阳底下无新鲜事。软件行业已经发展了这么多年，你想到的东西，肯定有人都想到过了。</p>

<p>每个软件也都有它的生命期，我们只要在它的生命期内完成它的使命就行了。软件往往需要尽快的投入使用，然后在使用中演化。这个演化最大可能并不依靠你一个人的力量去推动。随着参与的人增加，人和人（指开发人员）的共性就会减少。每个人都看得懂可以充分接受，软件才不容易向坏的方面演化。</p>

<p>我们常常谈模块化，谈高内聚，低耦合。</p>

<p>本质上，就是如何管理复杂度。如何把一件很难的事情（开发一个软件），分解成小问题，分而治之。</p>

<p>这些小问题之间的千丝万缕的联系，是设计人员面临的最大难题。</p>

<p>有些原则听起来不错，但是坚持起来很难。</p>

<p>比如，让模块的输入输出没有副作用。你能让你的模块每个输入对对应着唯一输出吗？</p>

<p>又比如，让模块层次化。如果 A 模块依赖 B 模块，B 模块依赖 C 模块。一旦出现这个状态，你能保证 A 模块绝对和 C 模块隔绝吗？更有甚者，让三个模块循环依赖这种更糟糕的事情也并不鲜见。</p>

<p>抽象是个好东西。但借助不断的抽象，问题不断的包起来，演化成新的巨无霸，显然会让事情更糟。虽然最终可能真的能像搭积木一样去组装软件了。或是雇佣更多的程序员填表单一样的工作，相互不需要对方在做什么。但是，软件性能却下降到了不可以忍受的地步。bug 也隐藏的更久，更不可收拾。</p>

<p>好的设计，必须对问题有足够清晰的理解。有如庖丁解牛一般，把整个问题划开，在最薄弱的地方分离。其实，做到这点，也就够了。</p>

<p>解决这些问题，其实跟语言无关。语言之争是没有多大意义的。如开头所说，把设计做好，模块之间的关系，用足够简单的方式就能描述清楚了，大部分流行的开发语言都能做到。</p>

<p>用 C 来实作，而没有用它的近亲 C++ ，也是为了避免狭隘的争议：我们该用这个特性吗？该用那个特性吗？这个形式做是不是好点？那样会不会有更好的性能？</p>

<p>所谓开发效率，对于个人来说，语言之不同，是会有很大差异。但是那是实现层面的差异。对于完成设计，这个过程，效率和所用语言无关。</p>

<p>实现的阶段，程序员可不可以开心的放心的去完成那些接口，这就是衡量设计好不好的指标了。这个时候，一个高开发效率的语言有优势（更少的代码量），一个容易掌握的语言也有优势（可以让更多的人参于而少犯错误）。</p>

<p>对于我的团队，我会更乐于采用一种让实现人员更轻松的方式。不用理会太多的语言细节，不用在投入开发前学习更多的概念（尤其是这个项目独有的），不用特别严格的 code review 也可以允许大家提交新的代码，切不至于轻易的引入 bug 。</p>

<p>我相信，软件做到后面，设计人员不需要亲自写太多代码。虽然我现在每天还是大量的写，也并不觉得枯燥。</p>

<p>事必恭亲是不好，但并不是说，你给实现人员足够信任就可以放手的。真正让你放手的只能是，你做出了好的设计，无论是谁，他也写不坏它。这时，是你乐意自己写，还是多找几个同学帮忙写，已经不重要了。</p>
]]>
    </content>
</entry>
<entry>
    <title>武汉的黄牛还是实在</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2010/01/avatar.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=515" title="武汉的黄牛还是实在" />
    <id>tag:blog.codingnow.com,2010://1.515</id>
    
    <published>2010-01-16T14:33:53Z</published>
    <updated>2010-01-16T06:46:48Z</updated>
    
    <summary>周五请了一天假回武汉看 iMax 版 Avatar。听说上海不排一晚上是买不到票了。特地周四晚飞回家，周五起了个早去新民众。 中午 12 点 50 到的，一看，好家伙，买票的人排了一层从楼梯都排到楼下了。周六下午场都卖完了。正打算去吃个饭再来排队的。结果一黄牛过来，说要不要 13 点的。我一看表正好 12 点 55 。100 快一张成交。 不错，就是没吃中饭，爆米花充饥。 电影不错，iMax 3D 感觉是不一样。屏幕大，才有足够的纵深。坐的位置估计也满重要。反正我坐在中间靠后一点，感觉正好。 这部片子啊，就是看效果了。情节就那样了，但是设定很严谨。适合我们这样做游戏的人看。...</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>周五请了一天假回武汉看 iMax 版 Avatar。听说上海不排一晚上是买不到票了。特地周四晚飞回家，周五起了个早去新民众。</p>

<p>中午 12 点 50 到的，一看，好家伙，买票的人排了一层从楼梯都排到楼下了。周六下午场都卖完了。正打算去吃个饭再来排队的。结果一黄牛过来，说要不要 13 点的。我一看表正好 12 点 55 。100 快一张成交。</p>

<p>不错，就是没吃中饭，爆米花充饥。</p>

<p>电影不错，iMax 3D 感觉是不一样。屏幕大，才有足够的纵深。坐的位置估计也满重要。反正我坐在中间靠后一点，感觉正好。</p>

<p>这部片子啊，就是看效果了。情节就那样了，但是设定很严谨。适合我们这样做游戏的人看。</p>
]]>
        

    </content>
</entry>
<entry>
    <title>《The New C Standard》的新版下载</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2010/01/the_new_c_standard_1_2.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=514" title="《The New C Standard》的新版下载" />
    <id>tag:blog.codingnow.com,2010://1.514</id>
    
    <published>2010-01-12T14:28:14Z</published>
    <updated>2010-01-12T06:44:31Z</updated>
    
    <summary>前几天被翻出来介绍《The New C Standard》的老帖，貌似被作者看到了。 The changing shape of code in the next decade 看来我的 blog 还是还是有点影响力 :) 好多人都下载了那本书。 Derek Jones 同学提醒我书已经有了新的版本。我已更新了原帖的下载链接。 btw, 机器翻译真是恐怖啊。当然，毕竟这位同学填对了 7 。 :D...</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>前几天被翻出来介绍《The New C Standard》的老帖，貌似被作者看到了。
<a href="http://shape-of-code.coding-guidelines.com/2009/12/the-changing-shape-of-code-in-the-next-decade/">The changing shape of code in the next decade</a></p>

<p>看来我的 blog 还是还是有点影响力 :) 好多人都下载了那本书。</p>

<p><a href="http://shape-of-code.coding-guidelines.com/">Derek Jones</a> 同学提醒我书已经有了新的版本。我已更新了原帖的下载链接。</p>

<p>btw, 机器翻译真是恐怖啊。当然，毕竟这位同学填对了 7 。 :D</p>
]]>
        

    </content>
</entry>
<entry>
    <title>Lua 5.2.0 (work1)</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2010/01/lua_520_work1.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=513" title="Lua 5.2.0 (work1)" />
    <id>tag:blog.codingnow.com,2010://1.513</id>
    
    <published>2010-01-12T13:54:40Z</published>
    <updated>2010-01-12T06:11:54Z</updated>
    
    <summary>[ANN] Lua 5.2.0 (work1) now available ，这个消息有几天了。lua 社区这两天非常热闹，各大牛都现身了。 做 LuaJIT 的牛人 Mike Pall 对 bit 库没有采用他做好的现成方案那可是相当的不客气。 不过，欢呼雀跃的人还是比较多的。每次 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><a href="http://lua-users.org/lists/lua-l/2010-01/msg00260.html">[ANN] Lua 5.2.0 (work1) now available</a> ，这个消息有几天了。lua 社区这两天非常热闹，各大牛都现身了。</p>

<p>做 LuaJIT 的牛人 Mike Pall 对 bit 库没有采用他做好的现成方案那可是<a href="http://lua-users.org/lists/lua-l/2010-01/msg00290.html">相当的不客气</a>。 </p>

<p>不过，欢呼雀跃的人还是比较多的。每次 lua 升级个小版本，改动都非常大。对成熟项目，不给你伤点筋骨，那就不是 lua 三巨头的风格了。当然，对于时不时重写代码的我，欣赏这种风格 ;) 我喜欢更健康的 lua 语言。</p>

<p>嗯，无论如何，lua 的源代码是非常值得阅读的。</p>
]]>
        <![CDATA[<p>这次把 setfenv/getfenv 给取消了。我想这会牵扯许多项目的代码，包括我自己的。当然，想兼容的话，稍微改改也成。</p>

<p>不过新加的 in exp do block end 语法是我喜欢的东西。比用 setfenv 去做一些 DSL 的活方便多了。主要是不太容易出错，代码也更漂亮了。</p>

<p>这个玩意如果用 setfenv 去模拟，就难看多了。可以参考我<a href="http://blog.codingnow.com/cloud/LuaTips">前几年写的短篇</a> 最后，模拟 pascal 中的 with 这段。</p>

<p>其它许多改动，对老代码都或多或少会有点影响。但是可以促使大家把代码重新写的更规范一些，好事儿。</p>

<p>我想，把代码写正确的话，性能也会提高一点的吧。</p>
]]>
    </content>
</entry>
<entry>
    <title>随便写写</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2010/01/book.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=512" title="随便写写" />
    <id>tag:blog.codingnow.com,2010://1.512</id>
    
    <published>2010-01-08T16:08:43Z</published>
    <updated>2010-01-08T08:12:53Z</updated>
    
    <summary>这些年来，经常会收到前些年写的那本书的读者来信。最近有些同学抱怨说买不到了，有的想让我寄一本。我只能说，谢谢，我这里也没有。估计，出版社也不会再刷了。如果真不想看电子版，可以去图书馆复印一本。其实写过技术类书的同学都知道，写书并不是件好差使，也绝对不可能靠这个赚钱。基本上，你投入的精力，是金钱上的回报远远不及的。真正的意义或许在分享了自己的知识，多交了几个朋友吧。 话说那本书，我自己是不太满意的。04 年写的时候，基本只能总结 03 年之前的一些东西，还差许多火候。写完之后，我给自己打了 70 分，慢慢的，觉得有 60 分就不错了。勉强及格吧。...</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://blog.codingnow.com/cloud/PrintingErrors">前些年写的那本书</a>的读者来信。最近有些同学抱怨说买不到了，有的想让我寄一本。我只能说，谢谢，我这里也没有。估计，出版社也不会再刷了。如果真不想看电子版，可以去图书馆复印一本。其实写过技术类书的同学都知道，写书并不是件好差使，也绝对不可能靠这个赚钱。基本上，你投入的精力，是金钱上的回报远远不及的。真正的意义或许在分享了自己的知识，多交了几个朋友吧。</p>

<p>话说那本书，我自己是不太满意的。04 年写的时候，基本只能总结 03 年之前的一些东西，还差许多火候。写完之后，我给自己打了 70 分，慢慢的，觉得有 60 分就不错了。勉强及格吧。</p>
]]>
        <![CDATA[<p>这几年一直有念头写点别的东西。源于这几年做项目的诸多经历。觉得自己悟到了不少东西，可又总感觉还总结不好。一开始想译<a href="http://blog.codingnow.com/2009/01/the_new_c_standard.html">一本关于 C 语言的书</a>，没开始就被那厚度吓坏了。一些做书的同学又建议我还不如自己写。是啊，我翻了一下，最有名的，谭公的《C 语言程序设计》，无语。据说很多人想再写一本，我就不参合了。有什么能超过 K&amp;R 的《C程序设计语言》呢？</p>

<p>其实，写语言是次要的东西。除了 C++ ，我看没几种语言需要有一本以上谈语言的书了。关键是借语言谈点事儿。我们并不是用不好语言，是不知道到底该怎么解决复杂性这个问题。</p>

<p>我们会有很多方法解决问题，可能也不会存在某种通用的伎俩。不过每个有 10 年以上编程经验的程序员，只要他不是天天重复昨天。那么总会有自己的一套东西，值得拿出来供人参考一下。可以是编程习惯，编码风格，常用的范式，刻意规避的问题。</p>

<p>近三年来，我用 C 语言堆砌了大量的代码。想了很多问题。加上之前一共 19 年（1991 年到 2010 年）的 C/C++ 经验，总算觉得稍微摸到点门道。唉，最近项目还是太忙了。忙过了，真想写写，总结一下。绕开各种语言表面的浮华，看清问题的本质，用 C 这样貌似简陋的语言工具，去构建一个由许多人共同维护的复杂项目，还是一件挺有趣的事情啊。</p>

<p>问题的实质往往不是你是否能找到一个精巧的结构，完美的表达它；而是找到一个最通俗易懂，不需要同伴付出额外学习负担的方式。所谓“大巧若拙”吧。</p>
]]>
    </content>
</entry>
<entry>
    <title>点光源的管理</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/12/point_light_management.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=511" title="点光源的管理" />
    <id>tag:blog.codingnow.com,2009://1.511</id>
    
    <published>2009-12-29T14:03:59Z</published>
    <updated>2009-12-29T06:27:08Z</updated>
    
    <summary>我们 3d engine 的点光源相关的代码，以前设计的是比较糟糕的。最近几天，我决定自己动手重新设计和实现这块东西。性能倒是次要的东西，重要的是要把模块分离，减低耦合度。 这个光源模块设计要解决的问题在于： GPU 的处理能力，目前看来比较有限，不可能实时处理无限数量的光源。所以，当你的场景里设置了许多的光源时，必须可以拣选出最可能影响被渲染物体的光源信息，把这个信息交给驱动去处理。...</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>我们 3d engine 的点光源相关的代码，以前设计的是比较糟糕的。最近几天，我决定自己动手重新设计和实现这块东西。性能倒是次要的东西，重要的是要把模块分离，减低耦合度。</p>

<p>这个光源模块设计要解决的问题在于：</p>

<p>GPU 的处理能力，目前看来比较有限，不可能实时处理无限数量的光源。所以，当你的场景里设置了许多的光源时，必须可以拣选出最可能影响被渲染物体的光源信息，把这个信息交给驱动去处理。</p>
]]>
        <![CDATA[<p>前任负责人在实现时，把光源揉杂在场景管理模块和渲染模块中去写，虽然功能实现了，但是维护麻烦。而且里面有很多需要性能优化的地方，由于结构设计不佳，导致动起来比较困难。更别说换个人去维护了。</p>

<p>增加关于光的新特性也会相对复杂。</p>

<p>经过一些思考后，我决定重新设计和实现。</p>

<hr />

<p>虚拟场景是树状层次结构的，这个是很自然的设计。但是树状结构也比较难操作。而光源的管理，假设不处理复杂的遮挡（暂时我们游戏也没这个需求，有的话，也不违背以下设计），其实我们要的仅仅是知道所有激活光源在世界中的绝对位置。</p>

<p>所以，我把光源的位置管理藏在光的管理模块内部，在接口上并不暴露出这一信息。</p>

<p>作为光管理模块，我们需要的仅仅是把逐个光源设置进它自己内部维护的一个虚拟场景中（无层次结构）。然后在真正渲染时，取回空间中某个位置附近的光源而已。</p>

<p>为了给以后的优化留下足够的信息。我增加了一个受光体的概念。受光体保留自己在光场景中位置。我们向光的管理模块提供受光体对象，而不直接提交位置信息。这样可以留下 cache 层，加快查询速度。</p>

<hr />

<p>这样，渲染流程就变成了：遍历场景树，将每个场景节点提交到下一层次。若是可渲染物，就提交到了渲染队列；若是光源，就把光源提交到了光管理模块。</p>

<p>在低一个层次，处理渲染队列时，经过若干排序和筛选操作后，真正渲染前，依据每个渲染物绑定的受光体对象，去光管理模块查询附近的光源，把这些光源包含的光信息提交给驱动层。</p>

<hr />

<p>这里可以做的优化：</p>

<p>每个渲染帧，从接口上看，其实都从头构建了光源和受光体构成的场景（藏于光管理模块的内部，独立于场景树）。我们可以 cache 上一帧的场景，并加以比较。对于不变的对象，从受光体的内部取出以前计算好的值，直接返回。</p>

<p>光源位置管理，可以使用八叉树。如果是固定第三人称视角的游戏，四叉树也足够了。或者更简单一点，使用网格。设置光源坐标时，帧之间加上脏标记，可以轻易的获知是否需要重复运算。这个优化手段，可以把所谓静态光源和动态光源的处理细节隐藏在接口的背后。</p>

<p>打格子的方法可能是最简单有效的优化手段，比用其它复杂的算法计算一个受光体附近最近的若干光源更符合 KISS 原则。为了使格子简单有效，可以把格子的边长设置为略小于一个点光源大约可以影响的半径之内。每个点光源提交的时候，以围绕其坐标，以等边三角形的方位，提交三次即可。最终，只需要遍历受光体所在格子，就可以快速查到其附近的光源。</p>

<p>当然，一开始，我们不需要作任何优化手段。用最苯的 O(N*N) 的算法，检索所有提交的光源即可。先做对，再做好。</p>
]]>
    </content>
</entry>
<entry>
    <title>最近玩的几个游戏</title>
    <link rel="alternate" type="text/html" href="http://blog.codingnow.com/2009/12/boardgame.html" />
    <link rel="service.edit" type="application/atom+xml" href="http://linode.codingnow.com/cgi-bin/mt/mt-atom.cgi/weblog/blog_id=1/entry_id=510" title="最近玩的几个游戏" />
    <id>tag:blog.codingnow.com,2009://1.510</id>
    
    <published>2009-12-25T11:48:29Z</published>
    <updated>2010-01-08T08:11:04Z</updated>
    
    <summary>上周末去上海参加了华东桌游嘉年华。 之前在杭州赛区赢了几个项目的预赛，拿到入场卷，所以门票就免了。最后以至于票多出来都没人要了。 赛场是在一个 LOFT 里，上面透风。同去的 A 同学的女朋友一直叫冷，我觉得还好，把外套借出去了。没想到感冒了，差点失声。估计另一个原因是我讲太多话了。 我在第一场现代艺术预赛里拿了５人组的第２。没得到第一纯粹是被一个不会玩的 mm 害了。然后去参加电力公司的复赛。由于时间太长，只好放弃了现代艺术的后续赛程。一开始有点可惜，后来听 A 同学讲述了半决赛经历后，我认为我们都没戏争夺冠军的。...</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>之前在杭州赛区赢了几个项目的预赛，拿到入场卷，所以门票就免了。最后以至于票多出来都没人要了。</p>

<p>赛场是在一个 LOFT 里，上面透风。同去的 A 同学的女朋友一直叫冷，我觉得还好，把外套借出去了。没想到感冒了，差点失声。估计另一个原因是我讲太多话了。</p>

<p>我在第一场现代艺术预赛里拿了５人组的第２。没得到第一纯粹是被一个不会玩的 mm 害了。然后去参加电力公司的复赛。由于时间太长，只好放弃了现代艺术的后续赛程。一开始有点可惜，后来听 A 同学讲述了半决赛经历后，我认为我们都没戏争夺冠军的。</p>
]]>
        <![CDATA[<p>几场比赛都深刻的领略了专业／职业玩家的实力。电力公司我参加的一个三人组，亏我一度以为赢定了，结果居然是第三 -_-</p>

<p>不过这个游戏我们玩的都不多，也就是打打酱油罢了。</p>

<p>前段因为出国旅游，错过了杭州赛区的报名，没得拿到银河竞逐的比赛入场卷。这个应该算是我个人最好的项目了吧。</p>

<p>第一天一直缠着主持人让他给我加个名额。只到第２天等到 C 组有人没到场，我才参了进去。可惜一开始友谊赛比的好好的，一正式比赛就输了 :( 还输的挺惨，拿到我有史以来最低分。中间算错对手一张牌，然后就崩盘了。毕竟还是跟高手打少了。我们自己人都玩的太休闲，经验是足够的，就是没真的参加什么比赛而已。没啥胜负心，所以也就嗑不下来。</p>

<p>当然，没加扩展包，也是很不习惯的。</p>

<hr />

<p>这次学会不少新游戏，感觉非常爽快。最后还拍到几款想要的游戏，价格还算公道。</p>

<p>其中比较喜欢的是《历史巨轮》，就是一 文明 on table 。流程相当的长，我们玩短版都从晚饭后一直玩到半夜两点。想找到时间登上月球，实现共产主义，估计得一整天了。 :D</p>

<p>这个周末打算花一天时间来开《 Star Craft 》，这次自己买了，可以在公司开。</p>

<hr />

<p>其实，桌面游戏的玩家群还真不小呢。</p>
]]>
    </content>
</entry>

</feed> 

