<?xml version="1.0" encoding="UTF-8" ?><rss version="2.0" xmlns:content="http://purl.org/Rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"><channel><title>主题:编程 文章</title><link>http://www.i170.com/sub/947/Rss</link><description>软件的编程技术，编程感受等</description><language>zh-cn</language><pubDate>Fri, 09 Jan 2009 18:02:03  +0800</pubDate><generator>i170.com</generator><image><title>主题:编程 文章</title><url>http://www.i170.com/Config/images/cf_logo.gif</url><link>http://www.i170.com/sub/947/Rss</link></image> <item><link>http://www.i170.com/Article/113798</link><title><![CDATA[OGRE 中的 Singleton]]></title><author>killercat</author><category></category><pubDate>Tue, 06 Jan 2009 02:36:45  +0800</pubDate><description><![CDATA[<p>OGRE 中的 Singleton 比较特别，所有的 Singleton 都继承于一个 Singleton 模板：</p>
<p>// Singleton 模板</p>
<p>template &lt;typename T&gt;<br>
class Singleton<br>
{<br>
public:<br>
&nbsp;&nbsp;&nbsp; Singleton()<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
assert(!ms_singleton);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ms_singleton =
static_cast&lt;T*&gt;(this);<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; ~Singleton()<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
assert(ms_singleton);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ms_singleton = 0;<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; static T&amp; GetSingleton()<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
assert(ms_singleton);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return
*ms_singleton;<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; static T* GetSingletonPtr()<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ms_singleton;<br>
&nbsp;&nbsp;&nbsp; }<br>
protected:<br>
&nbsp;&nbsp;&nbsp; static T* ms_singleton;<br>
};</p>
<p>// 使用模板（省略了原有的大量代码）</p>
<p>class _OgreExport LogManager : public
Singleton&lt;LogManager&gt;, public LogAlloc<br>
{<br>
public:</p>
<p>&nbsp;&nbsp;&nbsp; LogManager();<br>
&nbsp;&nbsp;&nbsp; ~LogManager();<br>
&nbsp;&nbsp;&nbsp; static LogManager* GetSingletonPtr(void);</p>
<p>&nbsp;&nbsp;&nbsp; static LogManager&amp;
GetSingleton(void);<br>
};</p>
<p>&nbsp;</p>
<p>OGRE 中使用引擎本身去调用构造函数的方式初始化 Singleton，而我们常见的 Singleton 都是第一次调用
GetSingleton 来创建实例，另外 Singleton 本身也不负责释放
ms_singleton。实际上，分离构造和析构使得更加容易使用在多线程的环境中，读者可以阅读 ACE 中 Singleton
的实现方式（n 种实现）。</p>
<p>笔者做了一点小的修改：</p>
<p>#define SINGLETON_DECLARE(T) static T&amp; GetSingleton();
static T* GetSingletonPtr();<br>
#define SINGLETON_IMPLEMENT(T) template&lt;&gt; T*
Singleton&lt;T&gt;::ms_singleton = 0;\<br>
&nbsp;&nbsp;&nbsp; T* T::GetSingletonPtr() { assert(ms_singleton);
return ms_singleton; } \<br>
&nbsp;&nbsp;&nbsp; T&amp; T::GetSingleton() { assert(ms_singleton);
return *ms_singleton; }<br>
<br>
template &lt;typename T&gt;<br>
class Singleton<br>
{<br>
protected:<br>
&nbsp;&nbsp;&nbsp; Singleton()<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
assert(!ms_singleton);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ms_singleton =
static_cast&lt;T*&gt;(this);<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; ~Singleton()<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
assert(ms_singleton);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ms_singleton = 0;<br>
&nbsp;&nbsp;&nbsp; }<br>
protected:<br>
&nbsp;&nbsp;&nbsp; static T* ms_singleton;<br>
<br>
&nbsp;&nbsp;&nbsp; // Hide<br>
private:<br>
&nbsp;&nbsp;&nbsp; Singleton(const Singleton&amp;);<br>
&nbsp;&nbsp;&nbsp; void operator=(const Singleton&amp;);<br>
};</p>
<p>// 使用代码</p>
<p>class MyMgr : public Singleton&lt;MyMgr&gt;<br>
{<br>
public:<br>
&nbsp;&nbsp;&nbsp; SINGLETON_DECLARE(MyMgr)<br>
&nbsp;&nbsp;&nbsp; void SayHello();<br>
};<br>
<br>
SINGLETON_IMPLEMENT(MyMgr)<br>
<br>
void MyMgr::SayHello()<br>
{<br>
&nbsp;&nbsp;&nbsp; cout &lt;&lt; "Hello world!" &lt;&lt; endl;<br>
}</p>

]]></description><guid>http://www.i170.com/Article/113798</guid><trackback:ping>http://www.i170.com/Article/113798/trackback</trackback:ping><comments>http://www.i170.com/Article/113798#comment</comments><wfw:commentRss>http://www.i170.com/Article/113798/commentRss</wfw:commentRss></item> <item><link>http://www.i170.com/Article/113724</link><title><![CDATA[C++ 命名规范]]></title><author>killercat</author><category></category><pubDate>Thu, 01 Jan 2009 17:12:19  +0800</pubDate><description><![CDATA[<p>在一个项目中，命名风格保持一致，否则会降低源码的可读性。</p>
<p>&nbsp;</p>
<p>命名必须要注意的两点：</p>
<p>1）永远不要使用晦涩的名字</p>
<p>2）总是使用全大写字母表示宏和常量</p>
<p>&nbsp;</p>
<p>匈牙利命名法：</p>
<p>匈牙利命名法的一个特点是将类型信息混入名称中，例如：</p>
<p>int nNum = 0; // n 为类型信息，表明 nNum 是一个 int 类型</p>
<p>class CUser; // C 为类型信息，表明 CUser 是一个类</p>
<p>
通常的观点认为，在名称中加入类型信息会增加代码的可读性，实际上，这种效果甚微。加入类型信息的不良表现之一是降低可维护性，一个很简单的例子：</p>
<p>class CUser</p>
<p>{</p>
<p>private:</p>
<p>&nbsp;&nbsp;&nbsp; int m_nID;</p>
<p>}</p>
<p>如果出现某种情况导致需要修改 m_nID 的类型为 long，那么 m_nID 应该改名字为
m_lID，那么意味着要在整个类中修改 m_nID。更糟糕的情况是：</p>
<p>class CHome</p>
<p>{</p>
<p>public:</p>
<p>&nbsp;&nbsp;&nbsp; void f();</p>
<p>}</p>
<p>void CHome::f()</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; int nID = m_pUser-&gt;GetID(); // nID 需要改成
lID</p>
<p>&nbsp;&nbsp; // 下面大量使用 nID</p>
<p>}</p>
<p>可见，CHome 中的 f 函数的局部变量 nID 在 id 类型由 int 变成 long 时也受到了影响。</p>
<p>
总的来说是将类型信息引入名称中致使维护成本增加，我们本无需因为类型的改变维护名称，匈牙利命名法将类型绑定在名称上，使得必须在类型变动时维护名称。</p>
<p>在泛型编程中，几乎无法使用匈牙利命名法，因为具体的类型信息并不存在。</p>
<p>除非你使用纯文本编辑器，否则名称中加入类型信息的做法不会为你增加任何可读性（现代的 IDE
甚至是一些高级文本编辑器，在你用鼠标点击到变量名称上时，就能显示变量的类型），它已经过时了。</p>
<p>&nbsp;</p>
<p>一套可行的命名规范：</p>
<p>1）类名、枚举名、结构名、联合名、typedef 定义的类型名、函数名可以使用 LikeThis 的命名方式。</p>
<p>2）变量名使用诸如 likeThis 的命名方式（首字母小写）</p>
<p>3）类成员变量名前加 m_ 例如：</p>
<p>class User</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; int m_id;</p>
<p>}</p>
<p>全局变量前加 g_ 例如：</p>
<p>int g_id;</p>
<p>静态变量使用 s_ 例如：</p>
<p>static int s_id;</p>
<p>这里说明一下，为什么需要把作用域和链接性信息融入名称中：</p>
<p>&lt;1&gt;变量作用域和链接性改变的情况是很少的，例如，很少的情况下会把一个成员变量改成静态变量</p>
<p>&lt;2&gt;编程中使用的工具常常不会直观的显示变量的作用域和链接性</p>
<p>&lt;3&gt;在许多高质量的代码中，均不同程度的将作用域信息融入变量名称中（通常使用 "_" 来表明其作用域）</p>
<p>对于静态类成员变量，通常使用如下方式表示：</p>
<p>template &lt;typename T&gt;</p>
<p>class Singleton</p>
<p>{</p>
<p>protected:</p>
<p>&nbsp;&nbsp;&nbsp; static T* ms_instance; // 作用域前于链接性</p>
<p>}</p>
<p>4）宏和常量使用 LIKE_THIS 的命名方式</p>
<p>5）namespace 使用小写字母命名</p>

]]></description><guid>http://www.i170.com/Article/113724</guid><trackback:ping>http://www.i170.com/Article/113724/trackback</trackback:ping><comments>http://www.i170.com/Article/113724#comment</comments><wfw:commentRss>http://www.i170.com/Article/113724/commentRss</wfw:commentRss></item> <item><link>http://www.i170.com/Article/113666</link><title><![CDATA[敏捷开发 --- 敏捷宣言]]></title><author>killercat</author><category></category><pubDate>Tue, 30 Dec 2008 11:31:23  +0800</pubDate><description><![CDATA[<p>1. 个体和交互胜过过程和工具</p>
<p>
个体和交互：一个优秀团队由具有较强交互能力和编程能力的成员组成，其中交互能力重要性胜于编程能力。所谓的交互并不限于人与人面对面的交流，还可以是其他多种方式。</p>
<p>工具：使用简单而不是复杂的工具，直到有足够理由表明需要使用更多的功能才进行更换。</p>
<p>&nbsp;</p>
<p>2. 可以工作的软件胜过面面俱到的文档</p>
<p>文档在描述系统原理和结构相比代码更有优势，然而过多的文档将浪费大量的时间（编写和维护的时间），即一个团队应该包含的文档：</p>
<p>1）描述系统原理和结构的文档</p>
<p>2）代码（从代码中提起系统原理和结构信息可能是困难的）</p>
<p>人之间的交互是学习系统的原理和结构系统的最快的方式。</p>
<p>总的来说：直到迫切需要并且意义重大时，才编写文档。</p>
<p>&nbsp;</p>
<p>3. 客户合作胜过合同谈判</p>
<p>客户通过丢下一份关于你想要软件的描述，然后固定时间和价格让人开发，这通常会导致项目的失败。</p>
<p>成功的项目需要频繁的客户反馈，让开发团队和客户一起工作并且经常提供反馈。（开发团队和客户需要频繁的交互）</p>
<p>&nbsp;</p>
<p>4. 响应变化胜过遵循计划</p>
<p>
详细的计划在面临需求变动时，显得不再适用，较好的做法是计划为下两周做详细的计划，为下三个月做粗略的计划，之后的做极为粗略的计划。团队成员需要清楚最近应该做的详细的任务，粗略了解之后需要实现的需求。</p>

]]></description><guid>http://www.i170.com/Article/113666</guid><trackback:ping>http://www.i170.com/Article/113666/trackback</trackback:ping><comments>http://www.i170.com/Article/113666#comment</comments><wfw:commentRss>http://www.i170.com/Article/113666/commentRss</wfw:commentRss></item> <item><link>http://www.i170.com/Article/113625</link><title><![CDATA[C++ 中常见技巧 --- 节约内存]]></title><author>killercat</author><category></category><pubDate>Sat, 27 Dec 2008 18:53:46  +0800</pubDate><description><![CDATA[<p>C++ 中一种常见的节约内存的方式是让一个值附有多种含义，例如：</p>
<p>int&nbsp;day = 20081227 // 用一个数表示年、月、日</p>
<p>而不是我们常见的：</p>
<p>// 使用 3 个整数表示</p>
<p>int year;</p>
<p>int month;</p>
<p>int day;</p>
<p>可见第一种方式比较节约内存，在网络传输上有一定的优势，解析的时候，使用：</p>
<p>(day / 10000) % 10000 // 年</p>
<p>(day / 100) % 100 // 月</p>
<p>(day / 1) % 100 // 日</p>
<p>归纳起来就是：</p>
<p>对于内存中连续的区域（命名为 mem），把这段区域被分成 x 个固定大小组成部分（x &gt;=
1），每个组成部分称之为这个区域的一个块</p>
<p>如果使用整数来表示这个区域，则可以使用公式 (mem / (10 ^ n)) % (10 ^ m) 来解析各个块，这里 n
表示这个块最低位在整个区域中的位置，m 表示这个区域的长度。</p>
<p>如果使用非整数来表示这个区域，例如：</p>
<p>struct mem // 24 bits</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; char mem1;</p>
<p>&nbsp;&nbsp;&nbsp; char mem2;</p>
<p>&nbsp;&nbsp;&nbsp; char mem3;</p>
<p>};</p>
<p>解析起来就稍显麻烦了。我们可以使用 STL 的&nbsp;bitset 而不是上面的结构，那样更加方便。</p>
<p>但是最终，我还是不建议使用此类方式来节约内存，原因有一下几点：</p>
<p>1）通常情况下，内存不会是稀缺资源</p>
<p>2）使用上述方式解决必定使得源码可读性降低</p>
<p>3）使用者必须遵循原定契约（每个块多长，表示什么意思），契约的出现必定增加一定的复杂度，使得错误更加容易产生</p>
<p>因此，除非必要，否则不要使用这种手段。</p>
<p>&nbsp;</p>
<p>另外，我们还可以看到一种按位使用的情况，即是：每一位表示一种状态</p>
<p>01 // 第一位表示男性还是女性，第二位表示是否结婚，那么 01 就表示未婚男性</p>
<p>上面是一个简单的例子，实际使用中可能有更加复杂的情况，我们在如下情况下可以使用：</p>
<p>1）状态只有正反两面</p>
<p>2）状态相互独立（最好能满足这条）</p>
<p>3）状态数量很多（最好能满足这条）</p>
<p>在满足上述 3 个条件的情况下，使用也不会带来多大的坏处，甚至比别的实现方式更好。</p>

]]></description><guid>http://www.i170.com/Article/113625</guid><trackback:ping>http://www.i170.com/Article/113625/trackback</trackback:ping><comments>http://www.i170.com/Article/113625#comment</comments><wfw:commentRss>http://www.i170.com/Article/113625/commentRss</wfw:commentRss></item> <item><link>http://www.i170.com/Article/113555</link><title><![CDATA[再论 C/C++ 变长参数]]></title><author>killercat</author><category></category><pubDate>Wed, 24 Dec 2008 11:20:09  +0800</pubDate><description><![CDATA[<p>首先需要明白一些基础知识，那么再来看变长参数的使用：</p>
<p>1）参数压栈的时候，如果是整形，那么小于 4 字节的都将提升为 4 字节，例如：</p>
<p>char c = 'a';</p>
<p>printf("%d", c); // 这里实参 c 将提升为 4 字节</p>
<p>另外 bool 类型也将提升为整形</p>
<p>float 类型将提升为 double 类型</p>
<p>2）所有参数在内存上是连续的，例如：</p>
<p>int a, b;</p>
<p>f(a, b); // 这里 a, b 两个变量的地址之差是 sizeof(int)</p>
<p>&nbsp;</p>
<p>看一些问题：</p>
<p>bool first = true;<br>
int second = 10;</p>
<p>// 输出 1 10，这里 first 被提升为 4 字节<br>
printf("%d %d", first, second);</p>
<p>&nbsp;</p>
<p>// 输出有问题的情况</p>
<p>__int64 first = 20; // __int64 在笔者机器上是 8 字节<br>
int second = 10;</p>
<p>// 输出 10 0，这里 %d %d 取的是 first 的低 4 位和高 4 位，而没有取到 second<br>
printf("%d %d", first, second);</p>
<p>&nbsp;</p>
<p>// 浮点类型</p>
<p>float first = 20;<br>
int second = 10;</p>
<p>// 输出 0 1077149696，由于浮点类型的表示不同于整形，所以输出此结果</p>
<p>// 但依然是取 first 的低 4 位和高 4 位，这里 first 首先提升为 double 类型<br>
printf("%d %d", first, second);</p>
<p>&nbsp;</p>
<p>我们看一下变长参数的实现：</p>
<p>// 保证 _INTSIZEOF(n) 的结果为：如果 sizeof(n) 小于 sizeof(int) 则为
sizeof(int)</p>
<p>#define _INTSIZEOF(n)&nbsp;&nbsp; ( (sizeof(n) + sizeof(int) -
1) &amp; ~(sizeof(int) - 1) )<br>
#define va_start(ap,v)&nbsp; ( ap = (va_list)&amp;v + _INTSIZEOF(v)
)<br>
#define va_arg(ap,t)&nbsp;&nbsp;&nbsp; ( *(t *)((ap +=
_INTSIZEOF(t)) - _INTSIZEOF(t)) )<br>
#define va_end(ap)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ( ap = (va_list)0
)</p>
<p>&nbsp;</p>
<p>仔细分析 #define _INTSIZEOF(n)&nbsp;&nbsp; ( (sizeof(n) +
sizeof(int) - 1) &amp; ~(sizeof(int) - 1) )</p>
<p>这里 ~(sizeof(int) - 1) 保证了最后 sizeof(int) - 1 位是 0，其余为 1，那么在 int 为
4 字节的环境中，其结果就是 1111 1111 1111 1111 1111 1111 1111 1100 那么同
sizeof(n) + sizeof(int) - 1 相 &amp; 的结果是后 2 位必须为 0，也就是说，只能为 4，8，12
...</p>
<p>另外，还需要说一下：</p>
<p>#define va_arg(ap,t)&nbsp;&nbsp;&nbsp; ( *(t *)((ap +=
_INTSIZEOF(t)) - _INTSIZEOF(t)) )</p>
<p>这里 ap 是一个指针，ap += _INTSIZEOF(t) 又减 _INTSIZEOF(t)，也就是说 va_arg
运算的结果就是当前 ap 的地址，然后在运算结束之后，ap 已经移动了 _INTSIZEOF(t) 通过 +=
运算。换一种表示就是：</p>
<p>tmp = ap;</p>
<p>ap += _INTSIZEOF(t);</p>
<p>return tmp;</p>

]]></description><guid>http://www.i170.com/Article/113555</guid><trackback:ping>http://www.i170.com/Article/113555/trackback</trackback:ping><comments>http://www.i170.com/Article/113555#comment</comments><wfw:commentRss>http://www.i170.com/Article/113555/commentRss</wfw:commentRss></item> <item><link>http://www.i170.com/Article/113520</link><title><![CDATA[Lua 迭代器和 for]]></title><author>killercat</author><category></category><pubDate>Mon, 22 Dec 2008 21:27:01  +0800</pubDate><description><![CDATA[<p>1. 关于本主题的一些基本概念：</p>
<p>1）Lua 中使用函数来表示迭代器</p>
<p>2）为了迭代的进行必须保存状态用于表示当前在容器中的位置此外还需要保存迭代的对象</p>
<p>3）for 的文法如下</p>
<p>for &lt;var-list&gt; in &lt;exp-list&gt; do</p>
<p>&nbsp;&nbsp;&nbsp; &lt;body&gt;</p>
<p>end</p>
<p>泛型 for（generic for）会保存 3 个值：</p>
<p>&lt;1&gt; 迭代函数（迭代器）</p>
<p>&lt;2&gt; 不变状态（invariant state）</p>
<p>&lt;3&gt; 控制变量（var-list 中的第一个变量就是控制变量）</p>
<p>这 3 个值由 exp-list 计算得来且仅计算一次，换而言之 exp-list 将初始化 for 循环保存的 3 个值，而这
3 个值中，通常只有控制变量会在循环中改变。如果 exp-list 计算出来的值多于 3 个，那么就被丢弃，少于 3 个将补
nil。</p>
<p>控制变量如果它为 nil，循环结束。这里 var-list 是迭代器返回值。</p>
<p>
注意，每次调用迭代器，不变状态和控制变量将作为参数传递到迭代器中。那么迭代器函数（f），不变状态（is），控制变量的关系就是（cv）：</p>
<p>cv = f(is, cv)</p>
<p>
这里详细阐述一下迭代器和控制变量的关系，迭代器的第一个返回值将赋值给控制变量，控制变量又将作为迭代器的第二个参数传递到迭代器中。</p>
<p>在不同的用法中，不变状态通常都被赋值为一个容器</p>
<p>&nbsp;</p>
<p>2. 使用闭包表示迭代器时</p>
<p>1）状态保存在外部局部变量（external local variable）中</p>
<p>2）迭代器总是被迭代器工厂创建</p>
<p>3）每一次调用 for 循环都需要创建一个闭包，在某些情况下，可能需要考虑性能问题</p>
<p>4）使用闭包表示迭代器是一种最简单的方式，它的实现不需要遵循任何契约</p>
<p>5）范例</p>
<p>arr = {"one", "two", "three", "four"}<br>
<br>
function iter_factory(arr)<br>
&nbsp;&nbsp;&nbsp; local curr_index = 0<br>
&nbsp;&nbsp;&nbsp; local len = table.getn(arr)<br>
&nbsp;&nbsp;&nbsp; return function ()<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; curr_index = curr_index +
1<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if curr_index &lt;= len then
return arr[curr_index] end<br>
&nbsp;&nbsp;&nbsp; end<br>
end<br>
<br>
for v in iter_factory(arr) do<br>
&nbsp;&nbsp;&nbsp; print(v)<br>
end<br>
&nbsp;</p>
<p>3. 使用普通函数</p>
<p>1）状态由泛型 for（generic for）来保存，具体来说是由 for
的控制变量来保存状态，用不变状态来保存迭代的对象（collection）</p>
<p>2）迭代器不需要在每次循环时创建而是作为一个函数的返回值</p>
<p>3）性能相比闭包情况时要好</p>
<p>4）范例</p>
<p>arr = {"one", "two", "three", "four"}<br>
<br>
function iter(arr, cur_index)<br>
&nbsp;&nbsp;&nbsp; cur_index = cur_index + 1<br>
&nbsp;&nbsp;&nbsp; local v = arr[cur_index]<br>
&nbsp;&nbsp;&nbsp; -- must return cur_index first(control
variable)<br>
&nbsp;&nbsp;&nbsp; if v then return cur_index, arr[cur_index]
end<br>
end<br>
<br>
function iter_init(arr)<br>
&nbsp;&nbsp;&nbsp; -- return iterator, invariant state, init
control variable<br>
&nbsp;&nbsp;&nbsp; return iter, arr, 0<br>
end<br>
<br>
for i, v in iter_init(arr) do<br>
&nbsp;&nbsp;&nbsp; print(v)<br>
end</p>
<p>&nbsp;</p>
<p>4. 其他</p>
<p>
本文不介绍多状态的迭代器，因为多状态的迭代器并不优雅，创建表的开销要大于创建闭包的开销，访问表的域也比访问外部局部变量慢，如果需要多状态时，建议使用闭包实现方式。</p>

]]></description><guid>http://www.i170.com/Article/113520</guid><trackback:ping>http://www.i170.com/Article/113520/trackback</trackback:ping><comments>http://www.i170.com/Article/113520#comment</comments><wfw:commentRss>http://www.i170.com/Article/113520/commentRss</wfw:commentRss></item> <item><link>http://www.i170.com/Article/113425</link><title><![CDATA[Lua 陷阱 --- 递归函数定义]]></title><author>killercat</author><category></category><pubDate>Fri, 19 Dec 2008 15:43:46  +0800</pubDate><description><![CDATA[<p>局部函数如此定义将失败：</p>
<p>local fact = function (n)<br>
&nbsp;&nbsp; &nbsp;if n == 0 then<br>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; return 1<br>
&nbsp;&nbsp; &nbsp;else<br>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; -- 这里 fact 还没有被创建<br>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; return n * fact(n - 1)<br>
&nbsp;&nbsp; &nbsp;end<br>
end</p>
<p>&nbsp;</p>
<p>使用以下语法定义局部函数：</p>
<p>local function fact(n)<br>
&nbsp;&nbsp;&nbsp; if n == 0 then<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return 1<br>
&nbsp;&nbsp;&nbsp; else</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -- Ok<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return n * fact(n - 1)<br>
&nbsp;&nbsp;&nbsp; end<br>
end</p>
<p>&nbsp;</p>
<p>另外说明一下函数定义的前置声明（forward declaration）：</p>
<p>-- 前置声明<br>
local f, g<br>
&nbsp;<br>
function g ()<br>
&nbsp;&nbsp;&nbsp; f()<br>
end<br>
&nbsp;&nbsp;&nbsp;<br>
function f ()<br>
&nbsp;&nbsp;&nbsp; g()<br>
end</p>

]]></description><guid>http://www.i170.com/Article/113425</guid><trackback:ping>http://www.i170.com/Article/113425/trackback</trackback:ping><comments>http://www.i170.com/Article/113425#comment</comments><wfw:commentRss>http://www.i170.com/Article/113425/commentRss</wfw:commentRss></item> <item><link>http://www.i170.com/Article/113399</link><title><![CDATA[Lua 中闭包的常见用法]]></title><author>killercat</author><category></category><pubDate>Thu, 18 Dec 2008 20:56:20  +0800</pubDate><description><![CDATA[<p>1. 使用闭包代替 table 会稍显高效</p>
<p>-- use table<br>
p = {1, 2}<br>
&nbsp;<br>
-- use closure<br>
function point(x, y)<br>
&nbsp;&nbsp;&nbsp; return function (index)<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if index == "x" then return
x<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; elseif index == "y" then
return y<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; else return x, y end<br>
&nbsp;&nbsp;&nbsp; end<br>
end<br>
&nbsp;<br>
p = point(1, 2)</p>
<p>&nbsp;</p>
<p>2. 使用闭包来完全替换一个函数的实现</p>
<p>-- 替换 math.sin 的实现，原有的 math.sin 实现被完全隐藏并提供了新的实现（使用度数而非弧度）</p>
<p>do<br>
&nbsp;&nbsp;&nbsp; local oldSin = math.sin<br>
&nbsp;&nbsp;&nbsp; local k = math.pi / 180<br>
&nbsp;&nbsp;&nbsp; math.sin = function (x)<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return oldSin(x * k)<br>
&nbsp;&nbsp;&nbsp; end<br>
end</p>
<p>&nbsp;</p>
<p>-- 替换 io.open 的实现，使得我们可以控制对文件的访问</p>
<p>do<br>
&nbsp;&nbsp;&nbsp; local oldOpen = io.open<br>
&nbsp;&nbsp;&nbsp; io.open = function (filename, mode)<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; -- access_OK 指定那些文件可以访问<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if access_OK(filename, mode)
then<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return
oldOpen(filename, mode)<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; else<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return
nil, "access denied"<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; end<br>
&nbsp;&nbsp;&nbsp; end<br>
end</p>
<p>&nbsp;</p>
<p>3. 函数的参数使用闭包</p>
<p>函数的参数使用闭包时，效果像 lambda 表达式：</p>
<p>network = {<br>
&nbsp;&nbsp;&nbsp; {name = "grauna",&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; IP = "210.26.30.34"},<br>
&nbsp;&nbsp;&nbsp; {name = "arraial",&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp; IP = "210.26.30.23"},<br>
&nbsp;&nbsp;&nbsp; {name = "lua",&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; IP =
"210.26.23.12"},<br>
&nbsp;&nbsp;&nbsp; {name = "derain",&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; IP = "210.26.23.20"},<br>
}</p>
<p>-- table.sort 就是一个典型的例子<br>
table.sort(network, function (a,b)<br>
&nbsp;&nbsp;&nbsp; return (a.name &gt; b.name)<br>
end)</p>

]]></description><guid>http://www.i170.com/Article/113399</guid><trackback:ping>http://www.i170.com/Article/113399/trackback</trackback:ping><comments>http://www.i170.com/Article/113399#comment</comments><wfw:commentRss>http://www.i170.com/Article/113399/commentRss</wfw:commentRss></item> <item><link>http://www.i170.com/Article/113374</link><title><![CDATA[shared_ptr 陷阱之 cycles of shared_ptr instances]]></title><author>killercat</author><category></category><pubDate>Wed, 17 Dec 2008 15:01:43  +0800</pubDate><description><![CDATA[<p>shared_ptr cycles of shared_ptr
instances（不知道如何翻译）将导致资源无法正确释放（cycles of <strong>shared_ptr</strong>
instances will not be reclaimed）</p>
<p>cycles of shared_ptr instances 的意思是：一个 shared_ptr
实例直接或者间接的保存了自身的实例，例如：</p>
<p>// ------------------------</p>
<p>class CTest<br>
{<br>
public:<br>
&nbsp;&nbsp;&nbsp; ~CTest();<br>
&nbsp;&nbsp;&nbsp; void SetNext(boost::shared_ptr&lt;CTest&gt;
pNext);<br>
private:<br>
&nbsp;&nbsp;&nbsp; boost::shared_ptr&lt;CTest&gt; m_pNext;<br>
};</p>
<p>// 资源无法释放</p>
<p>int main()<br>
{</p>
<p>&nbsp;&nbsp;&nbsp; // pTest 的引用计数为 1<br>
&nbsp;&nbsp;&nbsp; boost::shared_ptr&lt;CTest&gt; pTest(new
CTest);</p>
<p>&nbsp;&nbsp;&nbsp; // pTest 的引用计数为 2<br>
&nbsp;&nbsp;&nbsp; pTest-&gt;SetNext(pTest);<br>
&nbsp;&nbsp;&nbsp; return 0;</p>
<p>&nbsp;&nbsp;&nbsp; // pTest 析构之后，应用计数减 1，资源未释放（泄漏）<br>
}</p>
<p>// ------------------------</p>
<p>&nbsp;</p>
<p>在大量使用 shared_ptr 时，必须注意这个问题，我们可以使用 <a href=
"http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/weak_ptr.htm">weak_ptr</a>
来解决这个问题。</p>

]]></description><guid>http://www.i170.com/Article/113374</guid><trackback:ping>http://www.i170.com/Article/113374/trackback</trackback:ping><comments>http://www.i170.com/Article/113374#comment</comments><wfw:commentRss>http://www.i170.com/Article/113374/commentRss</wfw:commentRss></item> <item><link>http://www.i170.com/Article/113044</link><title><![CDATA[Oracle PL/SQL 一些方便性用法]]></title><author>liangar</author><category></category><pubDate>Wed, 03 Dec 2008 18:34:30  +0800</pubDate><description><![CDATA[<pre>
PL/SQL是对SQL的扩展。
SQL是描述性语言，PL/SQL给数据库加入了过程控制。平常所说的存储过程、函数、触发器都属于PL/SQL。

对于创建存储过程,函数,触发器的创建,修改,Oracle也没有什么奇特之处.可以参照语法手册即可.

Oracle PL/SQL提供了一些容易使用的特性:

游标调用，Oracle提供了很多控制方式，如下方式比较简洁：
<font color="#0000FF">DECLARE</font>
        a0 X.a0%<font color="#0000FF">TYPE</font>;
        a1 X.a1%<font color="#0000FF">TYPE</font>;
        <font color="#0000FF">CURSOR</font> c <font color=
"#0000FF">IS SELECT</font> * <font color="#0000FF">FROM</font> X;
<font color="#0000FF">BEGIN</font>
        <font color="#0000FF">FOR</font> x0 <font color=
"#0000FF">IN</font> c <font color="#0000FF">LOOP</font>
                a0 := x0.a0;
                a1 := x0.a1;
        <font color="#0000FF">END LOOP</font>;
<font color="#0000FF">END</font>;

<strong>简洁的原因</strong>:
x0不需要声明
在<font color="#0000FF">FOR</font>语句时,游标自动打开
在出循环之后就自动关闭了
</pre>
<pre>
<strong>又注意到</strong>:
a0,a1的数据类型,说明与X表的a0,a1数据类型相同.这就是<font color=
"#0000FF"><strong>%TYPE</strong></font>的妙用了.
</pre>
<pre>
如果说明一个结构变量与X相同,可以用<font color=
"#0000FF"><strong>%ROWTYPE</strong></font>,如下:
x0 X<font color="#0000FF">%ROWTYPE</font>
其实,程序中的x0就是X<font color="#0000FF">%ROWTYPE</font>类型.在<font color=
"#0000FF">FETCH</font>时放入了值.如果用<font color=
"#0000FF">OPEN</font>显式操作,则可以:
<font color="#0000FF">FETCH</font> c <font color=
"#0000FF">INTO</font> x0;
一次性将所有值放入.
</pre>
]]></description><guid>http://www.i170.com/Article/113044</guid><trackback:ping>http://www.i170.com/Article/113044/trackback</trackback:ping><comments>http://www.i170.com/Article/113044#comment</comments><wfw:commentRss>http://www.i170.com/Article/113044/commentRss</wfw:commentRss></item> <item><link>http://www.i170.com/Article/112941</link><title><![CDATA[C++ 函数模板中的一点问题]]></title><author>killercat</author><category></category><pubDate>Sat, 29 Nov 2008 21:18:42  +0800</pubDate><description><![CDATA[<p>C++ 本来就比较复杂，因为加入了模板而变得非常复杂。我们来看一个《C++ Templates》上的例子：</p>
<p>// 比较任意的两个对象的大小，需要 operator&lt; 支持<br>
template &lt;typename T&gt;<br>
inline T const&amp; max(T const&amp; a, T const&amp; b)<br>
{<br>
&nbsp;return a &lt; b ? b : a;<br>
}</p>
<p>// 用于比较两个 C 风格的字符串<br>
inline char const* max(char const* a, char const* b)<br>
{<br>
&nbsp;return std::strcmp(a, b) &lt; 0 ? b : a;<br>
}</p>
<p>// 比较任意的三个对象的大小<br>
template &lt;typename T&gt;<br>
inline T const&amp; max(T const&amp; a, T const&amp; b, T
const&amp; c)<br>
{<br>
&nbsp;return max(max(a, b), c);<br>
}</p>
<p>&nbsp;</p>
<p>int main()<br>
{<br>
&nbsp;char const* pszName1 = "killercat1";<br>
&nbsp;char const* pszName2 = "killercat2";<br>
&nbsp;char const* pszName3 = "killercat3";</p>
<p>&nbsp;// 编译器警告：返回局部变量或临时变量的地址<br>
&nbsp;std::cout &lt;&lt; max(pszName1, pszName2, pszName3);</p>
<p>&nbsp;return 0;<br>
}</p>
<p>出现警告的原因在于 return max(max(a, b), c) 中的 max 函数编译器选择了 char const*
max(char const* a, char const* b)
这个版本，而这个版本返回的是一个临时变量，这个临时变量被三参的函数模板 max 返回出去，这显然出现了问题。</p>
<p>《C++
Templates》上面给出了建议是：只改变那些需要改变的内容。这里笔者认为根本不应该提供一个非函数模板来表示特殊情况，而应该定义显示具体化的模板：</p>
<p>template&lt;&gt;<br>
inline char const* const&amp; max(char const* const&amp; a, char
const* const&amp; b)<br>
{<br>
&nbsp;return std::strcmp(a, b) &lt; 0 ? b : a;<br>
}</p>
<p>至少我们在写出这样的代码时：</p>
<p>template&lt;&gt;<br>
inline char const* max(char const* a, char const* b)<br>
{<br>
&nbsp;return std::strcmp(a, b) &lt; 0 ? b : a;<br>
}</p>
<p>编译器会报错。</p>

]]></description><guid>http://www.i170.com/Article/112941</guid><trackback:ping>http://www.i170.com/Article/112941/trackback</trackback:ping><comments>http://www.i170.com/Article/112941#comment</comments><wfw:commentRss>http://www.i170.com/Article/112941/commentRss</wfw:commentRss></item> <item><link>http://www.i170.com/Article/112845</link><title><![CDATA[跨操作系统的C++类设计]]></title><author>liangar</author><category></category><pubDate>Tue, 25 Nov 2008 10:41:22  +0800</pubDate><description><![CDATA[<p><strong>目标</strong></p>
<p>&nbsp;</p>
<p>跨OS有什么好处？</p>
<p>最大的好处就是降低开发成本。</p>
<p>
VC++是最好的c++研发工具，就是VC6版本，至今也难有敌手。但unix的研发人员也是人啊，没日没夜地用gdb命令找bug也不是好玩的。用跨OS的方略，则问题变得easy。</p>
<p>&nbsp;</p>
<p><strong>资源</strong></p>
<p>&nbsp;</p>
<p>我们使用的跨平台库的OS相关部分是从XmailServer中来的。</p>
<p>使用XmailServer有几个原因：<br>
够小，简洁，有效。</p>
<p>&nbsp;</p>
<p>一个高级程序员，完全可以在一天内读完XmailServer的跨平台部分的封装。</p>
<p>&nbsp;</p>
<p>XmailServer最近进行了升级。其源码将unix的进行了合并处理，有空请研究：<br>
<a href=
"http://www.xmailserver.org.cn/">http://www.xmailserver.org.cn/</a><br>

我用的是上一代的版本。</p>
<p>&nbsp;</p>
<p><strong>注意点</strong></p>
<p><br>
跨平台，不仅仅是跨OS，还有32/64的差别，需要考虑。当然，XmailServer在头文件中已经将数据类型剥离出来了，只要逐个检查，按具体环境设置即可。<br>

千万别省略这个步骤，不然，出了问题要花比该步骤长N倍的时间。</p>
<p>就命名为：xsys.*吧。</p>
<p>&nbsp;</p>
<p><strong>附件说明</strong></p>
<p><br>
我先上传了一点，供<a href=
"http://www.i170.com/Attach/F96FC855-D777-4638-8EE2-01605B036950">下载</a>。</p>
<p>开源的同学们编程没有问题，封装的接口不够抽象简洁，以至于普通程序员望而生畏。</p>
<p>这个的封装，可谓厚积薄发，平易近人，请观摩。</p>

]]></description><guid>http://www.i170.com/Article/112845</guid><trackback:ping>http://www.i170.com/Article/112845/trackback</trackback:ping><comments>http://www.i170.com/Article/112845#comment</comments><wfw:commentRss>http://www.i170.com/Article/112845/commentRss</wfw:commentRss></item> </channel></rss> 