﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-loop_in_codes-随笔分类-lua</title><link>http://www.cppblog.com/kevinlynx/category/7886.html</link><description>低调做技术__
C/C++\MMORPG服务器\模块架构__ TODO：linux env/read more books __Kevin Lynx</description><language>zh-cn</language><lastBuildDate>Tue, 07 Aug 2012 11:12:20 GMT</lastBuildDate><pubDate>Tue, 07 Aug 2012 11:12:20 GMT</pubDate><ttl>60</ttl><item><title>让wxListCtrl支持子item编辑</title><link>http://www.cppblog.com/kevinlynx/archive/2012/08/07/186581.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Tue, 07 Aug 2012 09:09:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2012/08/07/186581.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/186581.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2012/08/07/186581.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/186581.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/186581.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>我使用的wxLua版本信息为<code>wxLua 2.8.7.0 built with wxWidgets 2.8.8</code>，也就是LuaForWindows_v5.1.4-40.exe这个安装包里自带的wxLua。我不知道其他wxWidgets版本里wxListCtrl怎样，但我使用的版本里wxListCtrl是不支持编辑里面的子item的。在我使用的report模式下，子item也就是特定某一行一列的item。</p>

<p>google了一下，发现悲剧地需要自己实现，主要就是自己显示一个wxTextCtrl：</p>

<!-- more -->




<div class="highlight">
<pre><code class="lua"><span class="c1">--</span>
<span class="c1">-- file: wxListCtrlTextEdit.lua</span>
<span class="c1">-- author: Kevin Lynx</span>
<span class="c1">-- date: 08.06.2012</span>
<span class="c1">--</span>
<span class="kd">local</span> <span class="n">EditList</span> <span class="o">=</span> <span class="p">{}</span>

<span class="c1">-- get the column by an abs point</span>
<span class="k">function</span> <span class="nf">EditList</span><span class="p">:</span><span class="n">getColumn</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
    <span class="kd">local</span> <span class="n">cols</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">listctrl</span><span class="p">:</span><span class="n">GetColumnCount</span><span class="p">()</span>
    <span class="kd">local</span> <span class="n">cx</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">cols</span> <span class="o">-</span> <span class="mi">1</span> <span class="k">do</span>
        <span class="kd">local</span> <span class="n">w</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">listctrl</span><span class="p">:</span><span class="n">GetColumnWidth</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">x</span> <span class="o">&lt;=</span> <span class="n">cx</span> <span class="o">+</span> <span class="n">w</span> <span class="k">then</span> <span class="k">return</span> <span class="n">i</span> <span class="k">end</span>
        <span class="n">cx</span> <span class="o">=</span> <span class="n">cx</span> <span class="o">+</span> <span class="n">w</span>
    <span class="k">end</span>
    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
<span class="k">end</span>

<span class="c1">-- when a mouse down, show a text edit control </span>
<span class="k">function</span> <span class="nf">EditList</span><span class="p">:</span><span class="n">onLeftDown</span><span class="p">(</span><span class="n">evt</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">editor</span><span class="p">:</span><span class="n">IsShown</span><span class="p">()</span> <span class="k">then</span>
        <span class="n">self</span><span class="p">:</span><span class="n">closeEditor</span><span class="p">()</span>
    <span class="k">end</span>
    <span class="kd">local</span> <span class="n">p</span> <span class="o">=</span> <span class="n">evt</span><span class="p">:</span><span class="n">GetPoint</span><span class="p">()</span>
    <span class="kd">local</span> <span class="n">row</span> <span class="o">=</span> <span class="n">evt</span><span class="p">:</span><span class="n">GetIndex</span><span class="p">()</span>
    <span class="kd">local</span> <span class="n">col</span> <span class="o">=</span> <span class="n">self</span><span class="p">:</span><span class="n">getColumn</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">x</span><span class="p">)</span>
    <span class="kd">local</span> <span class="n">rect</span> <span class="o">=</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxListCtrlEx</span><span class="p">.</span><span class="n">GetSubItemRect</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">listctrl</span><span class="p">,</span> <span class="n">row</span><span class="p">,</span> <span class="n">col</span><span class="p">)</span>
    <span class="n">rect</span><span class="p">:</span><span class="n">SetHeight</span><span class="p">(</span><span class="n">rect</span><span class="p">:</span><span class="n">GetHeight</span><span class="p">()</span> <span class="o">+</span> <span class="mi">5</span><span class="p">)</span> <span class="c1">-- adjust</span>
    <span class="n">self</span><span class="p">.</span><span class="n">editor</span><span class="p">:</span><span class="n">SetSize</span><span class="p">(</span><span class="n">rect</span><span class="p">)</span>
    <span class="n">self</span><span class="p">.</span><span class="n">editor</span><span class="p">:</span><span class="n">Show</span><span class="p">()</span>
    <span class="n">self</span><span class="p">.</span><span class="n">editor</span><span class="p">:</span><span class="n">SetValue</span><span class="p">(</span><span class="n">wx</span><span class="p">.</span><span class="n">wxListCtrlEx</span><span class="p">.</span><span class="n">GetItemText</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">listctrl</span><span class="p">,</span> <span class="n">row</span><span class="p">,</span> <span class="n">col</span><span class="p">))</span>
    <span class="n">self</span><span class="p">.</span><span class="n">editor</span><span class="p">:</span><span class="n">SetFocus</span><span class="p">()</span>
    <span class="n">self</span><span class="p">.</span><span class="n">col</span> <span class="o">=</span> <span class="n">col</span>
    <span class="n">self</span><span class="p">.</span><span class="n">row</span> <span class="o">=</span> <span class="n">row</span>
<span class="k">end</span>

<span class="k">function</span> <span class="nf">EditList</span><span class="p">:</span><span class="n">closeEditor</span><span class="p">()</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="n">self</span><span class="p">.</span><span class="n">editor</span><span class="p">:</span><span class="n">IsShown</span><span class="p">()</span> <span class="k">then</span> <span class="k">return</span> <span class="k">end</span>
    <span class="n">self</span><span class="p">.</span><span class="n">editor</span><span class="p">:</span><span class="n">Hide</span><span class="p">()</span>
    <span class="n">self</span><span class="p">.</span><span class="n">listctrl</span><span class="p">:</span><span class="n">SetItem</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">row</span><span class="p">,</span> <span class="n">self</span><span class="p">.</span><span class="n">col</span><span class="p">,</span> <span class="n">self</span><span class="p">.</span><span class="n">editor</span><span class="p">:</span><span class="n">GetValue</span><span class="p">())</span>
<span class="k">end</span>

<span class="k">function</span> <span class="nf">EditList</span><span class="p">:</span><span class="n">initialize</span><span class="p">()</span>
    <span class="n">self</span><span class="p">.</span><span class="n">editor</span> <span class="o">=</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxTextCtrl</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">listctrl</span><span class="p">,</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxID_ANY</span><span class="p">,</span> <span class="s2">"</span><span class="s">"</span><span class="p">,</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxDefaultPosition</span><span class="p">,</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxDefaultSize</span><span class="p">,</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxTE_PROCESS_ENTER</span> <span class="o">+</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxTE_RICH2</span><span class="p">)</span>
    <span class="n">self</span><span class="p">.</span><span class="n">editor</span><span class="p">:</span><span class="n">Connect</span><span class="p">(</span><span class="n">wx</span><span class="p">.</span><span class="n">wxEVT_COMMAND_TEXT_ENTER</span><span class="p">,</span> <span class="k">function</span> <span class="p">()</span> <span class="n">self</span><span class="p">:</span><span class="n">closeEditor</span><span class="p">()</span> <span class="k">end</span><span class="p">)</span>
    <span class="c1">-- not work actually</span>
    <span class="n">self</span><span class="p">.</span><span class="n">editor</span><span class="p">:</span><span class="n">Connect</span><span class="p">(</span><span class="n">wx</span><span class="p">.</span><span class="n">wxEVT_COMMAND_KILL_FOCUS</span><span class="p">,</span> <span class="k">function</span> <span class="p">()</span> <span class="n">self</span><span class="p">:</span><span class="n">closeEditor</span><span class="p">()</span> <span class="k">end</span><span class="p">)</span>
    <span class="n">self</span><span class="p">.</span><span class="n">editor</span><span class="p">:</span><span class="n">Hide</span><span class="p">()</span>
<span class="k">end</span>

<span class="k">function</span> <span class="nf">wx</span><span class="p">.</span><span class="n">wxListCtrlTextEdit</span><span class="p">(</span><span class="n">listctrl</span><span class="p">)</span>
    <span class="kd">local</span> <span class="n">o</span> <span class="o">=</span> <span class="p">{</span>
        <span class="n">listctrl</span> <span class="o">=</span> <span class="n">listctrl</span><span class="p">,</span>
        <span class="n">editor</span> <span class="o">=</span> <span class="kc">nil</span><span class="p">,</span>
    <span class="p">}</span>
    <span class="kd">local</span> <span class="n">editlist</span> <span class="o">=</span> <span class="n">newObject</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="n">EditList</span><span class="p">)</span>
    <span class="n">editlist</span><span class="p">:</span><span class="n">initialize</span><span class="p">()</span>
    <span class="n">listctrl</span><span class="p">:</span><span class="n">Connect</span><span class="p">(</span><span class="n">wx</span><span class="p">.</span><span class="n">wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK</span><span class="p">,</span> <span class="k">function</span> <span class="p">(</span><span class="n">evt</span><span class="p">)</span> <span class="n">editlist</span><span class="p">:</span><span class="n">onLeftDown</span><span class="p">(</span><span class="n">evt</span><span class="p">)</span> <span class="k">end</span><span class="p">)</span>
    <span class="n">listctrl</span><span class="p">:</span><span class="n">Connect</span><span class="p">(</span><span class="n">wx</span><span class="p">.</span><span class="n">wxEVT_COMMAND_LIST_ITEM_FOCUSED</span><span class="p">,</span> <span class="k">function</span> <span class="p">()</span> <span class="n">editlist</span><span class="p">:</span><span class="n">closeEditor</span><span class="p">()</span> <span class="k">end</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">listctrl</span>
<span class="k">end</span>
</code></pre>
</div>


<p>其原理就是获取到当前鼠标点击所在的子item位置，然后在此位置显示一个wxEditCtrl即可。以上代码需要依赖我之前写的<a href="http://codemacro.com/2012/08/02/simple-oo-in-lua/">Lua里实现简单的类-对象</a>中的代码，同时依赖以下针对wxListCtrl的扩展接口：</p>

<div class="highlight">
<pre><code class="lua"><span class="c1">--</span>
<span class="c1">-- file: wxListCtrlExtend.lua</span>
<span class="c1">-- author: Kevin Lynx</span>
<span class="c1">-- date: 08.07.2012</span>
<span class="c1">-- brief: extend some util functions to wx.wxListCtrl</span>
<span class="c1">-- </span>
<span class="n">wx</span><span class="p">.</span><span class="n">wxListCtrlEx</span> <span class="o">=</span> <span class="p">{}</span>

<span class="k">function</span> <span class="nf">wx</span><span class="p">.</span><span class="n">wxListCtrlEx</span><span class="p">.</span><span class="n">GetSubItemRect</span><span class="p">(</span><span class="n">listctrl</span><span class="p">,</span> <span class="n">item</span><span class="p">,</span> <span class="n">col</span><span class="p">)</span>
    <span class="kd">local</span> <span class="n">rect</span> <span class="o">=</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxRect</span><span class="p">()</span>
    <span class="n">listctrl</span><span class="p">:</span><span class="n">GetItemRect</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="n">rect</span><span class="p">)</span>
    <span class="kd">local</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="kd">local</span> <span class="n">w</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">col</span> <span class="k">do</span>
        <span class="n">w</span> <span class="o">=</span> <span class="n">listctrl</span><span class="p">:</span><span class="n">GetColumnWidth</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">w</span>
    <span class="k">end</span>
    <span class="k">return</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxRect</span><span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="n">w</span><span class="p">,</span> <span class="n">rect</span><span class="p">:</span><span class="n">GetY</span><span class="p">(),</span> <span class="n">w</span><span class="p">,</span> <span class="n">rect</span><span class="p">:</span><span class="n">GetHeight</span><span class="p">())</span>
<span class="k">end</span>

<span class="k">function</span> <span class="nf">wx</span><span class="p">.</span><span class="n">wxListCtrlEx</span><span class="p">.</span><span class="n">GetItemText</span><span class="p">(</span><span class="n">listctrl</span><span class="p">,</span> <span class="n">item</span><span class="p">,</span> <span class="n">col</span><span class="p">)</span>
    <span class="kd">local</span> <span class="n">info</span> <span class="o">=</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxListItem</span><span class="p">()</span>
    <span class="n">info</span><span class="p">:</span><span class="n">SetId</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
    <span class="n">info</span><span class="p">:</span><span class="n">SetColumn</span><span class="p">(</span><span class="n">col</span><span class="p">)</span>
    <span class="n">info</span><span class="p">:</span><span class="n">SetMask</span><span class="p">(</span><span class="n">wx</span><span class="p">.</span><span class="n">wxLIST_MASK_TEXT</span><span class="p">)</span>
    <span class="n">listctrl</span><span class="p">:</span><span class="n">GetItem</span><span class="p">(</span><span class="n">info</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">info</span><span class="p">:</span><span class="n">GetText</span><span class="p">()</span>
<span class="k">end</span>
</code></pre>
</div>


<p>在我看到的wxWidgets官方文档里，其实wxListCtrl已经有<code>GetSubItemRect</code>接口，并且在另一些示例代码里，也看到了<code>GetItemText</code>接口，但是，我使用的版本里没有，所以只好自己写。基于以上，要使用这个可以支持编辑子item的wxListCtrl，可以：</p>

<div class="highlight">
<pre><code class="lua"><span class="n">list</span> <span class="o">=</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxListCtrlTextEdit</span><span class="p">(</span><span class="n">wx</span><span class="p">.</span><span class="n">wxListCtrl</span><span class="p">(</span><span class="n">dialog</span><span class="p">,</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxID_ANY</span><span class="p">,</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxDefaultPosition</span><span class="p">,</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxDefaultSize</span><span class="p">,</span> <span class="n">wx</span><span class="p">.</span><span class="n">wxLC_REPORT</span><span class="p">))</span>
</code></pre>
</div>


<p>也就是通过wx.wxListCtrlTextEdit这个函数做下处理，这个函数返回的是本身的wxListCtrl。当然更好的方式是使用继承之类的方式，开发一种新的控件，但在Lua中，针对usedata类型的扩展貌似只能这样了。</p>

<p>最好吐槽下，这个控件扩展其实很恶心。本来我打算当编辑控件失去焦点后就隐藏它，但是往编辑控件上注册KILL_FOCUS事件始终不起作用；我又打算弄个ESC键盘事件去手动取消，但显然wxTextCtrl是不支持键盘事件的。好吧，凑合用了。</p>

<p class="post-footer">
            原文地址：
            <a href="http://codemacro.com/2012/08/07/edit-item-wxlistctrl/">http://codemacro.com/2012/08/07/edit-item-wxlistctrl/</a><br />
            written by <a href="http://codemacro.com">Kevin Lynx</a>
            &nbsp;posted at <a href="http://codemacro.com">http://codemacro.com</a>
            </p>

</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/186581.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2012-08-07 17:09 <a href="http://www.cppblog.com/kevinlynx/archive/2012/08/07/186581.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>tolua的tolua_toxxx系列API设计</title><link>http://www.cppblog.com/kevinlynx/archive/2012/05/10/174460.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Thu, 10 May 2012 07:38:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2012/05/10/174460.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/174460.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2012/05/10/174460.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/174460.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/174460.html</trackback:ping><description><![CDATA[<p>原文链接：<a href="http://codemacro.com/2012/05/10/tolua-api/">http://codemacro.com/2012/05/10/tolua-api/
</a></p>
<p>我们使用tolua++手工绑定c/c++接口到lua中，在绑定的接口实现里，就需要取出传入的参数。tolua++中提供了一系列tolua_toxxx函数，例如：</p>
<div class="highlight"><pre><code class="c"><span class="n">lua_Number</span> <span class="n">tolua_tonumber</span><span class="p">(</span><span class="n">lua_State</span> <span class="o">*</span><span class="n">L</span><span class="p">,</span> <span class="kt">int</span> <span class="n">narg</span><span class="p">,</span> <span class="n">lua_Number</span> <span class="n">def</span><span class="p">)</span>
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">tolua_tostring</span><span class="p">(</span><span class="n">lua_State</span> <span class="o">*</span><span class="n">L</span><span class="p">,</span> <span class="kt">int</span> <span class="n">narg</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">def</span><span class="p">)</span>
</code></pre>
</div>
<p>这些函数都有一个def参数。乍一看，这些函数使用起来很简单。传入lua_State，传入参数在栈中的位置，然后再传一个<strong>失败</strong>后返回的默认值。</p>
<p>我重点要说的是这里这个<strong>失败</strong>，按正常程序员的理解，针对lua而言，什么情况下算失败呢？lua语言里函数参数支持不传，此时实参为nil，将nil转换为一个c类型必然失败；参数类型不正确算不算失败？你传一个user data，c里按数字来取，这也算失败。</p>
<!-- more -->
<p>这么简单的API还需要多纠结什么呢？然后我们浩浩荡荡地写了上百个接口，什么tolua_tostring/tolua_tonumber的使用少说也有500了吧？</p>
<p>然后有一天，服务器宕机了，空指针:</p>
<div class="highlight"><pre><code class="c"><span class="cm">/* 失败返回""，还能省空指针的判断 */</span>
<span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span> <span class="o">=</span> <span class="n">tolua_tostring</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="s">""</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">name</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'\0'</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* 空串总得判断吧 */</span>
 <span class="p">...</span>
<span class="p">}</span>
</code></pre>
</div>
<p>跟踪后发现，脚本里传入的是nil，这里的name取出来是NULL，而不是&#8221;&#8220;（的地址）。然后吐槽了一下这个API，辛苦地修改了所有类似代码，增加对空指针的判断。我没有多想。</p>
<p>故事继续，有一天服务器虽然没宕机，但功能不正常了:</p>
<div class="highlight"><pre><code class="c"><span class="kt">float</span> <span class="n">angle</span> <span class="o">=</span> <span class="p">(</span><span class="kt">float</span><span class="p">)</span> <span class="n">tolua_tonumber</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">PI</span><span class="p">);</span>
<span class="p">...</span>
</code></pre>
</div>
<p>这个意思是，这个函数的参数1默认是2*PI，什么是默认？lua里某函数参数不传，或传nil就是使用默认。因为不传的话，这个实参本身就是nil。但，tolua_tonumber的行为不是这样的，它的实现真是偷懒:</p>
<div class="highlight"><pre><code class="c"><span class="n">TOLUA_API</span> <span class="n">lua_Number</span> <span class="nf">tolua_tonumber</span> <span class="p">(</span><span class="n">lua_State</span><span class="o">*</span> <span class="n">L</span><span class="p">,</span> <span class="kt">int</span> <span class="n">narg</span><span class="p">,</span> <span class="n">lua_Number</span> <span class="n">def</span><span class="p">)</span>
<span class="p">{</span>
 <span class="k">return</span> <span class="n">lua_gettop</span><span class="p">(</span><span class="n">L</span><span class="p">)</span><span class="o">&lt;</span><span class="n">abs</span><span class="p">(</span><span class="n">narg</span><span class="p">)</span> <span class="o">?</span> <span class="n">def</span> <span class="o">:</span> <span class="n">lua_tonumber</span><span class="p">(</span><span class="n">L</span><span class="p">,</span><span class="n">narg</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">TOLUA_API</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="nf">tolua_tostring</span> <span class="p">(</span><span class="n">lua_State</span><span class="o">*</span> <span class="n">L</span><span class="p">,</span> <span class="kt">int</span> <span class="n">narg</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">def</span><span class="p">)</span>
<span class="p">{</span>
 <span class="k">return</span> <span class="n">lua_gettop</span><span class="p">(</span><span class="n">L</span><span class="p">)</span><span class="o">&lt;</span><span class="n">abs</span><span class="p">(</span><span class="n">narg</span><span class="p">)</span> <span class="o">?</span> <span class="n">def</span> <span class="o">:</span> <span class="n">lua_tostring</span><span class="p">(</span><span class="n">L</span><span class="p">,</span><span class="n">narg</span><span class="p">);</span>
<span class="p">}</span>
</code></pre>
</div>
<p>意思是，只有当你不传的时候，它才返回默认值，否则就交给lua的API来管，而lua这些API是不支持应用层的默认参数的，对于lua_tonumber错误时就返回0，lua_tostring错误时就返回NULL。</p>
<p>这种其行为和其带来的common sense不一致的API设计，实在让人蛋疼。什么是common sense呢？就像一个UI库里的按钮，我们都知道有click事件，hover事件，UI库的文档甚至都不需要解释什么是click什么是hover，因为大家看到这个东西，就有了共识，无需废话，这就是common sense。就像tolua的这些API，非常普通，大家一看都期待在意外情况下你能返回def值。但它竟然不是。实在不行，你可以模仿lua的check系列函数的实现嘛:</p>
<div class="highlight"><pre><code class="c"><span class="n">LUALIB_API</span> <span class="n">lua_Number</span> <span class="nf">luaL_checknumber</span> <span class="p">(</span><span class="n">lua_State</span> <span class="o">*</span><span class="n">L</span><span class="p">,</span> <span class="kt">int</span> <span class="n">narg</span><span class="p">)</span> <span class="p">{</span>
 <span class="n">lua_Number</span> <span class="n">d</span> <span class="o">=</span> <span class="n">lua_tonumber</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="n">narg</span><span class="p">);</span>
 <span class="k">if</span> <span class="p">(</span><span class="n">d</span> <span class="o">==</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">lua_isnumber</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="n">narg</span><span class="p">))</span> <span class="cm">/* avoid extra test when d is not 0 */</span>
 <span class="n">tag_error</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="n">narg</span><span class="p">,</span> <span class="n">LUA_TNUMBER</span><span class="p">);</span>
 <span class="k">return</span> <span class="n">d</span><span class="p">;</span>
<span class="p">}</span>
</code></pre>
</div>
<p>即，根本不用去检查栈问题，直接在lua_tonumber之后再做包装检查。何况，lua需要你去检查栈吗？当你访问了栈外的元素时，lua会自动返回一个全局常量luaO_nilobject:</p>
<div class="highlight"><pre><code class="c"><span class="k">static</span> <span class="n">TValue</span> <span class="o">*</span><span class="nf">index2adr</span><span class="p">(</span><span class="n">lua_State</span> <span class="o">*</span><span class="n">L</span><span class="p">,</span> <span class="kt">int</span> <span class="n">idx</span><span class="p">)</span> <span class="p">{</span>
 <span class="p">...</span>
 <span class="k">if</span> <span class="p">(</span><span class="n">o</span> <span class="o">&gt;=</span> <span class="n">L</span><span class="o">-&gt;</span><span class="n">top</span><span class="p">)</span> <span class="k">return</span> <span class="n">cast</span><span class="p">(</span><span class="n">TValue</span><span class="o">*</span><span class="p">,</span> <span class="n">luaO_nilobject</span><span class="p">);</span>
<span class="p">}</span>
</code></pre>
</div>
<p>另，程序悲剧也来源于臆想。</p><img src ="http://www.cppblog.com/kevinlynx/aggbug/174460.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2012-05-10 15:38 <a href="http://www.cppblog.com/kevinlynx/archive/2012/05/10/174460.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>传递Lua函数到C/C++中</title><link>http://www.cppblog.com/kevinlynx/archive/2011/04/24/144905.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sun, 24 Apr 2011 09:28:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2011/04/24/144905.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/144905.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2011/04/24/144905.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/144905.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/144905.html</trackback:ping><description><![CDATA[<div id="luac-c" class="document"><h1 class="title">&#x4F20;&#x9012;Lua&#x51FD;&#x6570;&#x5230;C/C++&#x4E2D;</h1><div id="id1" class="section"><h1>&#x95EE;&#x9898;</h1><p>&#x5728;Lua&#x4E2D;&#xFF0C;&#x56E0;&#x4E3A;&#x51FD;&#x6570;&#x4E5F;&#x662F;&#x7B2C;&#x4E00;&#x7C7B;&#x503C;&#xFF0C;&#x6240;&#x4EE5;&#x4F1A;&#x51FA;&#x73B0;&#x5C06;&#x51FD;&#x6570;&#x4F5C;&#x4E3A;&#x53E6;&#x4E00;&#x4E2A;&#x51FD;&#x6570;&#x7684;&#x53C2;&#x6570;&#xFF0C;&#x6216;&#x8005;&#x51FD;&#x6570;&#x4F5C;
&#x4E3A;&#x51FD;&#x6570;&#x7684;&#x8FD4;&#x56DE;&#x503C;&#x3002;&#x8FD9;&#x79CD;&#x673A;&#x5236;&#x5728;&#x5F88;&#x591A;&#x5730;&#x65B9;&#x90FD;&#x80FD;&#x4EE3;&#x7801;&#x66F4;&#x7075;&#x6D3B;&#x66F4;&#x7B80;&#x6D01;&#xFF0C;&#x4F8B;&#x5982;:</p><pre class="literal-block">
table.sort(table [,comp])
</pre><p>&#x8FD9;&#x91CC;&#x7684;comp&#x5C31;&#x8981;&#x6C42;&#x4F20;&#x5165;&#x4E00;&#x4E2A;&#x51FD;&#x6570;&#xFF0C;&#x6211;&#x4EEC;&#x5728;&#x8C03;&#x7528;&#x65F6;&#xFF0C;&#x5927;&#x6982;&#x4F1A;&#x6709;&#x5982;&#x4E0B;&#x5F62;&#x5F0F;:</p><pre class="literal-block">
table.sort(t, comp) -- &#x76F4;&#x63A5;&#x5199;&#x51FD;&#x6570;&#x540D;
table.sort(t, local_comp) -- &#x67D0;&#x4E2A;&#x5C40;&#x90E8;&#x51FD;&#x6570;
table.sort(t, function (a, b) xxx end ) -- &#x4E34;&#x65F6;&#x6784;&#x9020;&#x4E00;&#x4E2A;&#x533F;&#x540D;&#x51FD;&#x6570;
</pre><p>&#x5176;&#x4E2D;&#x6700;&#x540E;&#x4E00;&#x79CD;&#x65B9;&#x5F0F;&#x6700;&#x4E3A;&#x7075;&#x6D3B;&#xFF0C;&#x4EFB;&#x610F;&#x65F6;&#x5019;&#x5728;&#x9700;&#x8981;&#x7684;&#x65F6;&#x5019;&#x6784;&#x9020;&#x4E00;&#x4E2A;&#x533F;&#x540D;&#x51FD;&#x6570;&#x3002;&#x8FD9;&#x79CD;&#x5728;Lua&#x81EA;&#x8EAB;&#x7684;
&#x73AF;&#x5883;&#x4E2D;&#x4F7F;&#x7528;&#xFF0C;&#x81EA;&#x7136;&#x6CA1;&#x6709;&#x95EE;&#x9898;&#x3002;&#x4F46;&#x662F;&#xFF0C;&#x5F53;&#x6211;&#x4EEC;&#x5728;C/C++&#x4E2D;&#x6CE8;&#x518C;&#x4E00;&#x4E9B;&#x51FD;&#x6570;&#x5230;Lua&#x73AF;&#x5883;&#x4E2D;&#xFF0C;&#x800C;&#x8FD9;&#x4E9B;
&#x51FD;&#x6570;&#x4E5F;&#x9700;&#x8981;&#x4F7F;&#x7528;&#x51FD;&#x6570;&#x53C2;&#x6570;&#x7684;&#x65F6;&#x5019;&#xFF0C;&#x95EE;&#x9898;&#x5C31;&#x51FA;&#x6765;&#x4E86;&#x3002;</p><p>Lua&#x672C;&#x8EAB;&#x662F;&#x4E0D;&#x652F;&#x6301;&#x5C06;Lua&#x51FD;&#x6570;&#x4F5C;&#x4E3A;&#x51FD;&#x6570;&#x53C2;&#x6570;&#x4F20;&#x5165;C/C++&#x7684;&#xFF0C;&#x4E0D;&#x7BA1;&#x8FD9;&#x4E2A;&#x60F3;&#x8981;&#x4F20;&#x5165;&#x7684;&#x51FD;&#x6570;&#x662F;&#x5168;&#x5C40;&#x7684;
&#x3001;&#x5C40;&#x90E8;&#x7684;&#x3001;&#x6216;&#x8005;&#x533F;&#x540D;&#x7684;&#xFF08;&#x533F;&#x540D;&#x7684;&#x672C;&#x8D28;&#x4E0A;&#x4E5F;&#x7B97;&#x5C40;&#x90E8;&#x7684;&#xFF09;&#x3002;&#x4E00;&#x822C;&#x60C5;&#x51B5;&#x4E0B;&#xFF0C;&#x6211;&#x4EEC;&#x552F;&#x4E00;&#x7684;&#x4EA4;&#x4E92;&#x65B9;&#x5F0F;&#xFF0C;
&#x4E0D;&#x662F;&#x4F20;&#x5165;&#x4E00;&#x4E2A;&#x51FD;&#x6570;&#xFF0C;&#x800C;&#x662F;&#x4E00;&#x4E2A;&#x5168;&#x5C40;&#x51FD;&#x6570;&#x540D;&#x3002;C/C++&#x4FDD;&#x5B58;&#x8FD9;&#x4E2A;&#x51FD;&#x6570;&#x540D;&#xFF0C;&#x5728;&#x9700;&#x8981;&#x56DE;&#x8C03;Lua&#x7684;&#x65F6;&#x5019;&#xFF0C;
&#x5C31;&#x5728;Lua&#x5168;&#x5C40;&#x8868;&#x4E2D;&#x627E;&#x5230;&#x8FD9;&#x4E2A;&#x51FD;&#x6570;&#xFF08;&#x6839;&#x636E;&#x51FD;&#x6570;&#x540D;&#xFF09;&#xFF0C;&#x7136;&#x540E;&#x518D;&#x8C03;&#x7528;&#x4E4B;&#x3002;&#x60C5;&#x51B5;&#x5927;&#x81F4;&#x5982;&#x4E0B;:</p><pre class="literal-block">
function lua_func () xxx end
cfunc(lua_func) -- wrong
cfunc(&quot;lua_func&quot;) -- right
</pre><p>&#x6211;&#x4EEC;&#x8FD9;&#x56DE;&#x7684;&#x811A;&#x672C;&#x6A21;&#x5757;&#xFF0C;&#x7B56;&#x5212;&#x4F1A;&#x5927;&#x91CF;&#x4F7F;&#x7528;&#x9700;&#x8981;&#x56DE;&#x8C03;&#x51FD;&#x6570;&#x7684;C/C++&#x51FD;&#x6570;&#x3002;&#x663E;&#x7136;&#xFF0C;&#x521B;&#x5EFA;&#x5927;&#x91CF;&#x7684;&#x5168;&#x5C40;
&#x51FD;&#x6570;&#xFF0C;&#x5148;&#x662F;&#x4ECE;&#x5199;&#x4EE3;&#x7801;&#x7684;&#x89D2;&#x5EA6;&#x770B;&#xFF0C;&#x5C31;&#x662F;&#x5F88;&#x4F24;&#x795E;&#x7684;&#x3002;</p></div><div id="id2" class="section"><h1>&#x89E3;&#x51B3;</h1><p>&#x6211;&#x4EEC;&#x6700;&#x7EC8;&#x9700;&#x8981;&#x7684;&#x65B9;&#x5F0F;&#xFF0C;&#x5927;&#x6982;&#x5982;&#x4E0B;:</p><pre class="literal-block">
cfunc(lua_func) -- ok
cfunc(function () xxx end) -- ok
local xxx = function () xxx end
cfunc(xxx) -- ok
</pre><p>&#x8981;&#x89E3;&#x51B3;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;&#xFF0C;&#x6211;&#x7684;&#x601D;&#x8DEF;&#x662F;&#x76F4;&#x63A5;&#x5728;Lua&#x5C42;&#x505A;&#x4E00;&#x4E9B;&#x5305;&#x88C5;&#x3002;&#x56E0;&#x4E3A;C/C++&#x90A3;&#x8FB9;&#x4EC5;&#x652F;&#x6301;&#x4F20;&#x5165;&#x4E00;&#x4E2A;&#x5168;&#x5C40;
&#x51FD;&#x6570;&#x540D;&#xFF08;&#x5F53;&#x7136;&#x4E0D;&#x4E00;&#x5B9A;&#x5F97;&#x5168;&#x5C40;&#x7684;&#xFF0C;&#x6839;&#x636E;&#x5B9E;&#x9645;&#x60C5;&#x51B5;&#xFF0C;&#x53EF;&#x80FD;&#x5728;&#x5176;&#x4ED6;&#x81EA;&#x5DF1;&#x6784;&#x9020;&#x7684;&#x8868;&#x91CC;&#x4E5F;&#x884C;&#xFF09;&#xFF0C;&#x4E5F;&#x5C31;&#x662F;
&#x4E00;&#x4E2A;&#x5B57;&#x7B26;&#x4E32;&#xFF0C;&#x6240;&#x4EE5;&#x6211;&#x7684;&#x601D;&#x8DEF;&#x5C31;&#x662F;&#x5C06;Lua&#x51FD;&#x6570;&#x548C;&#x4E00;&#x4E2A;&#x552F;&#x4E00;&#x7684;&#x5B57;&#x7B26;&#x4E32;&#x505A;&#x6620;&#x5C04;&#x3002;:</p><pre class="literal-block">
function wrap (fn)
    local id = generate_id()
    local fn_s = &quot;__callback_fn&quot;..id
    _G[fn_s] = fn
    return fn_s
end
</pre><p>&#x8FD9;&#x4E2A;wrap&#x51FD;&#x6570;&#xFF0C;&#x5C31;&#x662F;&#x5C06;&#x4E00;&#x4E2A;&#x51FD;&#x6570;&#x5728;&#x5168;&#x5C40;&#x8868;&#x91CC;&#x6620;&#x5C04;&#x5230;&#x4E00;&#x4E2A;&#x5B57;&#x7B26;&#x4E32;&#x4E0A;&#xFF0C;&#x90A3;&#x4E48;&#x5728;&#x4F7F;&#x7528;&#x65F6;:</p><pre class="literal-block">
cfunc(wrap(function () xxx end))
cfunc(const char *fn_name, xxx); -- cfunc&#x7684;&#x539F;&#x578B;
</pre><p>cfunc&#x662F;C/C++&#x65B9;&#x6CE8;&#x518C;&#x8FDB;Lua&#x7684;&#x51FD;&#x6570;&#xFF0C;&#x5B83;&#x7684;&#x539F;&#x578B;&#x5F88;&#x4E2D;&#x89C4;&#x4E2D;&#x77E9;&#xFF0C;&#x5373;&#xFF1A;&#x53EA;&#x63A5;&#x6536;&#x4E00;&#x4E2A;&#x51FD;&#x6570;&#x540D;&#xFF0C;&#x4E00;&#x4E2A;&#x5B57;
&#x7B26;&#x4E32;&#xFF0C;&#x5982;&#x4E4B;&#x524D;&#x6240;&#x8BF4;&#xFF0C;C/C++&#x8981;&#x8C03;&#x7528;&#x8FD9;&#x4E2A;&#x56DE;&#x8C03;&#x51FD;&#x6570;&#x65F6;&#xFF0C;&#x5C31;&#x6839;&#x636E;&#x8FD9;&#x4E2A;&#x5B57;&#x7B26;&#x4E32;&#x53BB;&#x67E5;&#x627E;&#x5BF9;&#x5E94;&#x7684;&#x51FD;&#x6570;&#x3002;
&#x811A;&#x672C;&#x65B9;&#x5728;&#x8C03;&#x7528;&#x65F6;&#xFF0C;&#x5982;&#x679C;&#x60F3;&#x4F20;&#x5165;&#x4E00;&#x4E2A;&#x533F;&#x540D;&#x51FD;&#x6570;&#x4E86;&#xFF0C;&#x5C31;&#x8C03;&#x7528;wrap&#x51FD;&#x6570;&#x5305;&#x88C5;&#x4E00;&#x4E0B;&#x5373;&#x53EF;&#x3002;</p></div><div id="id3" class="section"><h1>&#x4E00;&#x4E2A;&#x6539;&#x8FDB;</h1><p>&#x4E0A;&#x9762;&#x7684;&#x65B9;&#x6CD5;&#x6709;&#x4E2A;&#x5F88;&#x4E25;&#x91CD;&#x7684;&#x95EE;&#x9898;&#xFF0C;&#x5728;&#x591A;&#x6B21;&#x8C03;&#x7528;wrap&#x51FD;&#x6570;&#x540E;&#xFF0C;&#x5C06;&#x5BFC;&#x81F4;&#x5168;&#x5C40;&#x8868;&#x4E5F;&#x968F;&#x4E4B;&#x81A8;&#x80C0;&#x3002;&#x6211;&#x4EEC;&#x9700;
&#x8981;&#x60F3;&#x529E;&#x6CD5;&#x5728;C/C++&#x5B8C;&#x6210;&#x56DE;&#x8C03;&#x540E;&#xFF0C;&#x6765;&#x6E05;&#x9664;wrap&#x5EFA;&#x7ACB;&#x7684;&#x6570;&#x636E;&#x3002;&#x8FD9;&#x4E2A;&#x5DE5;&#x4F5C;&#x5F53;&#x7136;&#x53EF;&#x4EE5;&#x653E;&#x5230;C/C++&#x6765;&#x8FDB;&#x884C;
&#xFF0C;&#x4F8B;&#x5982;&#x6BCF;&#x6B21;&#x53D1;&#x751F;&#x56DE;&#x8C03;&#x540E;&#xFF0C;&#x5C31;&#x8BBE;&#x7F6E;&#x4E0B;&#x5168;&#x5C40;&#x8868;&#x3002;&#x4F46;&#x8FD9;&#x660E;&#x663E;&#x662F;&#x4E0D;&#x5BF9;&#x7684;&#xFF0C;&#x56E0;&#x4E3A;&#x8FDD;&#x80CC;&#x4E86;&#x63A5;&#x53E3;&#x7684;&#x8BBE;&#x8BA1;&#x539F;&#x5219;
&#xFF0C;&#x8FD9;&#x4E2A;&#x989D;&#x5916;&#x7684;&#x673A;&#x5236;&#x662F;&#x5728;Lua&#x91CC;&#x6DFB;&#x52A0;&#x7684;&#xFF0C;&#x90A3;&#x4E48;&#x8D23;&#x4EFB;&#x4E5F;&#x6700;&#x597D;&#x7531;Lua&#x6765;&#x8D1F;&#x3002;&#x8981;&#x89E3;&#x51B3;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;&#xFF0C;&#x5C31;&#x53EF;&#x4EE5;
&#x4F7F;&#x7528;Lua&#x7684;metamethods&#x673A;&#x5236;&#x3002;&#x8FD9;&#x4E2A;&#x673A;&#x5236;&#x53EF;&#x4EE5;&#x5728;Lua&#x5185;&#x90E8;&#x53D1;&#x751F;&#x7279;&#x5B9A;&#x4E8B;&#x4EF6;&#x65F6;&#xFF0C;&#x8BA9;&#x5E94;&#x7528;&#x5C42;&#x5F97;&#x5230;&#x901A;&#x77E5;&#x3002;
&#x8FD9;&#x91CC;&#xFF0C;&#x6211;&#x4EEC;&#x9700;&#x8981;&#x5173;&#x6CE8;__call&#x4E8B;&#x4EF6;&#x3002;</p><p>Lua&#x4E2D;&#x53EA;&#x8981;&#x6709;__call metamethod&#x7684;&#x503C;&#xFF0C;&#x5747;&#x53EF;&#x88AB;&#x5F53;&#x4F5C;&#x51FD;&#x6570;&#x8C03;&#x7528;&#x3002;&#x4F8B;&#x5982;:</p><pre class="literal-block">
ab(1, 2)
</pre><p>&#x8FD9;&#x91CC;&#x8FD9;&#x4E2A;&#x51FD;&#x6570;&#x8C03;&#x7528;&#x5F62;&#x5F0F;&#xFF0C;Lua&#x5C31;&#x4F1A;&#x53BB;&#x627E;ab&#x662F;&#x5426;&#x6709;__call metamethod&#xFF0C;&#x5982;&#x679C;&#x6709;&#xFF0C;&#x5219;&#x8C03;&#x7528;&#x5B83;&#x3002;&#x8FD9;
&#x4E2A;&#x4E8B;&#x5B9E;&#x6697;&#x793A;&#x6211;&#x4EEC;&#xFF0C;&#x4E00;&#x4E2A;table&#x4E5F;&#x53EF;&#x4EE5;&#x88AB;&#x8C03;&#x7528;&#x3002;&#x4E00;&#x4E2A;&#x6539;&#x8FDB;&#x7684;wrap&#x51FD;&#x6570;&#x5982;&#x4E0B;:</p><pre class="literal-block">
local function create_callback_table (fn, name)
    local t = {}
    t.callback = fn
    setmetatable (t, {__call =  -- &#x5173;&#x6CE8;__call
        function (func, ...) -- &#x5728;t(xx)&#x65F6;&#xFF0C;&#x5C06;&#x8C03;&#x7528;&#x5230;&#x8FD9;&#x4E2A;&#x51FD;&#x6570;
            func.callback (...) -- &#x771F;&#x6B63;&#x7684;&#x56DE;&#x8C03;
            del_callback (name) -- &#x56DE;&#x8C03;&#x5B8C;&#x6BD5;&#xFF0C;&#x6E05;&#x9664;wrap&#x5EFA;&#x7ACB;&#x7684;&#x6570;&#x636E;
        end })
    return t
end

function wrap (fn)
    local id = generate_func_id() -- &#x4EA7;&#x751F;&#x552F;&#x4E00;&#x7684;id
    local fn_s = &quot;_callback_fn&quot;..id
    _G[fn_s] = create_callback_table(fn, fn_s) -- _G[fn_s]&#x5BF9;&#x5E94;&#x7684;&#x662F;&#x4E00;&#x4E2A;&#x8868;
    return fn_s
end
</pre><p>&#x5728;&#x6211;&#x4EEC;&#x7684;C/C++&#x7A0B;&#x5E8F;&#x4E2D;&#xFF0C;&#x4F9D;&#x7136;&#x5982;&#x5F80;&#x5E38;&#x4E00;&#x6837;&#xFF0C;&#x5148;&#x662F;&#x4ECE;_G&#x91CC;&#x53D6;&#x51FA;&#x51FD;&#x6570;&#x540D;&#x5BF9;&#x5E94;&#x7684;&#x5BF9;&#x8C61;&#x3002;&#x867D;&#x7136;&#x8FD9;&#x4E2A;&#x5BF9;
&#x8C61;&#x73B0;&#x5728;&#x5DF2;&#x7ECF;&#x662F;&#x4E00;&#x4E2A;table&#x3002;&#x7136;&#x540E;lua_call&#x3002;</p><p>&#x4E0A;&#x9762;&#x7684;&#x4EE3;&#x7801;&#x662F;&#x5426;&#x4F1A;&#x5728;&#x539F;&#x6709;&#x57FA;&#x7840;&#x4E0A;&#x589E;&#x52A0;&#x4E0D;&#x53EF;&#x63A5;&#x53D7;&#x7684;&#x6027;&#x80FD;&#x4EE3;&#x4EF7;&#xFF1F;&#x867D;&#x7136;&#x6211;&#x6CA1;&#x6709;&#x505A;&#x8FC7;&#x5B9E;&#x9645;&#x6D4B;&#x8BD5;&#xFF0C;&#x4F46;&#x662F;
&#x4ECE;&#x8868;&#x660E;&#x770B;&#x6765;&#xFF0C;&#x6392;&#x9664;meta table&#x5728;Lua&#x91CC;&#x7684;&#x4EE3;&#x4EF7;&#xFF0C;&#x4E5F;&#x5C31;&#x591A;&#x4E86;&#x51E0;&#x6B21;Lua&#x51FD;&#x6570;&#x8C03;&#x7528;&#x3002;</p><p>&#x6700;&#x540E;&#xFF0C;&#x611F;&#x53F9;&#x4E00;&#x4E0B;&#xFF0C;Lua&#x91CC;&#x7684;table&#x53CA;metatable&#x673A;&#x5236;&#xFF0C;&#x5B9E;&#x5728;&#x975E;&#x5E38;&#x5F3A;&#x5927;&#x3002;&#x8FD9;&#x79CD;&#x5F3A;&#x5927;&#x4E0D;&#x662F;&#x529F;&#x80FD;&#x5806;&#x780C;
&#x51FA;&#x6765;&#x7684;&#x5F3A;&#x5927;&#xFF0C;&#x800C;&#x662F;&#x7B80;&#x5355;&#x4E1C;&#x897F;&#x7EC4;&#x5408;&#x51FA;&#x6765;&#x7684;&#x5F3A;&#x5927;&#x3002;&#x5176;&#x80CC;&#x540E;&#x7684;&#x8BBE;&#x8BA1;&#x601D;&#x60F3;&#xFF0C;&#x7740;&#x5B9E;&#x8BA9;&#x4EBA;&#x4F69;&#x670D;&#x3002;</p><p><strong>4.26.2011 Update</strong></p><p>&#x4E4B;&#x524D;&#x7684;&#x6587;&#x4E2D;&#x8BF4;&#x201C;Lua&#x672C;&#x8EAB;&#x662F;&#x4E0D;&#x652F;&#x6301;&#x5C06;Lua&#x51FD;&#x6570;&#x4F5C;&#x4E3A;&#x51FD;&#x6570;&#x53C2;&#x6570;&#x4F20;&#x5165;C/C++&#x7684;&#x201C;&#xFF0C;&#x8FD9;&#x53E5;&#x8BDD;&#x4E25;&#x683C;&#x6765;&#x8BF4;&#x4E0D;
&#x6B63;&#x786E;&#xFF08;&#x7531;&#x67D0;&#x7F51;&#x53CB;&#x8BC4;&#x8BBA;&#xFF09;&#x3002;&#x5047;&#x8BBE;&#x51FD;&#x6570;cfun&#x7531;c/c++&#x6CE8;&#x518C;&#xFF0C;&#x6211;&#x4EEC;&#x662F;&#x53EF;&#x4EE5;&#x7F16;&#x5199;&#x5982;&#x4E0B;&#x4EE3;&#x7801;&#x7684;:</p><pre class="literal-block">
cfunc(print) -- &#x4F20;&#x5165;Lua&#x51FD;&#x6570;
</pre><p>&#x4F46;&#x662F;&#x95EE;&#x9898;&#x5728;&#x4E8E;&#xFF0C;&#x6211;&#x4EEC;&#x65E0;&#x6CD5;&#x53D6;&#x51FA;&#x8FD9;&#x4E2A;&#x51FD;&#x6570;&#x5E76;&#x4FDD;&#x5B58;&#x5728;c/c++&#x65B9;&#x3002;Lua&#x63D0;&#x4F9B;&#x4E86;&#x4E00;&#x4E9B;&#x63A5;&#x53E3;&#x7528;&#x4E8E;&#x53D6;cfunc
&#x7684;&#x53C2;&#x6570;&#xFF0C;&#x4F8B;&#x5982;luaL_checknumber&#xFF08;&#x5C01;&#x88C5;lua_tonumber&#xFF09;&#x3002;&#x4F46;&#x6CA1;&#x6709;&#x7C7B;&#x4F3C;luaL_checkfunction&#x7684;
&#x63A5;&#x53E3;&#x3002;Lua&#x4E2D;&#x7684;table&#x6709;&#x540C;&#x6837;&#x7684;&#x95EE;&#x9898;&#x3002;&#x7A76;&#x5176;&#x539F;&#x56E0;&#xFF0C;&#x4E3B;&#x8981;&#x662F;Lua&#x4E2D;&#x7684;&#x51FD;&#x6570;&#x6CA1;&#x6709;&#x76F4;&#x63A5;&#x7684;c/c++&#x6570;&#x636E;&#x7ED3;
&#x6784;&#x5BF9;&#x5E94;&#x3002;</p><p>;; END</p></div></div><img src ="http://www.cppblog.com/kevinlynx/aggbug/144905.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2011-04-24 17:28 <a href="http://www.cppblog.com/kevinlynx/archive/2011/04/24/144905.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>飞秋lua版：luafeiq0.1.0发布</title><link>http://www.cppblog.com/kevinlynx/archive/2011/01/31/139646.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Mon, 31 Jan 2011 08:51:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2011/01/31/139646.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/139646.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2011/01/31/139646.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/139646.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/139646.html</trackback:ping><description><![CDATA[<p><font size="2">继上次捣鼓出了<a href="http://www.cppblog.com/kevinlynx/archive/2011/01/23/139187.html" target="_blank">飞秋的群聊协议</a>后,鉴于年底没啥事情做,就用lua写了个简单的协议兼容的IM。本来开始让</font></p>
<p><font size="2">另一个同事在iptux的基础上修改的，结果大概是因为iptux的代码不是那么容易修改，就不了了之了。这个</font></p>
<p><font size="2">刚发布的<a href="http://code.google.com/p/luafeiq/" target="_blank">luafeiq</a>功能非常简单，仅支持与飞秋（包括大部分兼容IP messager的IM）进行单聊，群</font><font size="2">消息的</font></p>
<p><font size="2">收发，简易的消息盒子（暂存未读消息）。因为选的库都是跨平台的，所以很容易的luafeiq也是跨</font><font size="2">平台的，</font></p>
<p><font size="2">最主要的是我想在linux下使用。</font></p>
<p><font size="2"></font>&nbsp;</p>
<p><font size="2">之所以选用lua，一方面是想练练lua，另一方面则是因为开发效率。前段时间在<a href="http://kevinlynx.javaeye.com/" target="_blank">android下写了些java代码</a>，</font></p>
<p><font size="2">用java写代码觉得甚为爽快（当然算不了完美）。这几天写了千把行的lua（也许有3K行，未统计过），</font></p>
<p><font size="2">感觉也不错。综合来说，这些高级语言的很多好用的语法特性，例如闭包（closure），垃圾回收，都提高</font></p>
<p><font size="2">了不少写代码的速度。当然，lua于我而言也算不上完美的语言。例如我经常因为变量敲错字母，而在运行时</font></p>
<p><font size="2">才暴露nil错误。这也许可以通过诸如IDE之类的工具在写代码的时候就给予提示。lua 在遇到一个符号时，</font></p>
<p><font size="2">默认地将其处理为全局的。关于这个语法特性早有人提出不爽，只能说大家设计的准则不一样。（在我们</font></p>
<p><font size="2">项目里，我直接改写了全局变量的metatable，从而防止策划随意定义全局变量）</font></p>
<p><font size="2"></font>&nbsp;</p>
<p><font size="2">再来谈谈实现过程中的一些琐事。因为飞秋也算是IP messager协议的兼容实现，很多通信除了可以使用</font></p>
<p><font size="2">抓包软件分析外，还可以直接通过IP messager的源码来了解。所以，基础通信协议的实现过程也比较</font></p>
<p><font size="2">简单。飞秋与飞秋之间发送私聊消息是经过加密的。其加密过程也不简单，更重要的是，我并不想浪费太</font></p>
<p><font size="2">多时间在这上面。后来发现其实可以通过上线消息里某个标志位表明自己不需要加密。这个标志就是消息头</font></p>
<p><font size="2">里的option。上线广播出去的消息里一旦表明自己不加密，那么以后和飞秋通信也就不需要解密了。</font></p>
<p><font size="2"></font>&nbsp;</p>
<p><font size="2">发送私聊消息时，消息里会携带一个消息ID。这个ID可以通过任意算法生成，例如直接取time的值。接收到</font></p>
<p><font size="2">对方的消息时，需要取出该ID，然后加入回应消息。对方收到回应消息后，就知道自己发送成功。这个过程</font></p>
<p><font size="2">算是ip messager在UDP上做的消息可靠验证，过程也比较简单。</font></p>
<p><font size="2"></font>&nbsp;</p>
<p><font size="2">群聊消息在之前提到过，是通过UDP多播实现。我们可以接收所有群的消息。如果之前已经处于某个群里，</font></p>
<p><font size="2">那么一旦你上线后（广播上线消息），你就可以直接在这个群里发言。但如果你之前不在这个群里，则</font></p>
<p><font size="2">可以通过多播一个加入群的消息，然后就可以不请自来地在这个群里发言。详细的消息值和实现都可以从</font></p>
<p><font size="2">luafeiq的代码里读到（message_sender.lua）。</font></p>
<p><font size="2"></font>&nbsp;</p>
<p><font size="2">在linux下接收windows上的飞秋消息，是需要做字符编码转换的。因为luafeiq使用IUP作为UI库，IUP在</font></p>
<p><font size="2">linux下使用GTK作为底层实现，默认全部是UTF8编码。luafeiq里我自己写了个lua库，用于编码转换。</font></p>
<p><font size="2"></font>&nbsp;</p>
<p><font size="2">话说IUP作为一个UI库，还是比较不错的。正如其介绍文档里所说，学习曲线低，基本上看一会文档，就可以</font></p>
<p><font size="2">直接使用了。luafeiq使用的IUP版本至少需要3.0以上。当初在linux下为了安装IUP3.3，基本花了4个小时</font></p>
<p><font size="2">时间，各种奇怪的没多大意义的错误信息。后来换成3.2版本，居然一下子就和谐了，无限怨念。</font></p>
<p><font size="2"></font>&nbsp;</p>
<p><font size="2">luafeiq目前放在googlecode的版本，可以说是一个很不负责任的版本。早上我才刚把字符编码转换的代码</font></p>
<p><font size="2">调试好。今天已经请假，家里就一台电脑，也就测试不了这个字符编码转换是否真的能正常工作。我在</font></p>
<p><font size="2">windows下dump了些字符，看上去能正常功能。明天得回老家过春节，上不了网，索性就提前发布了。</font></p>
<p><font size="2"></font>&nbsp;</p>
<p><font size="2">luafeiq项目地址：<a title="http://code.google.com/p/luafeiq/" href="http://code.google.com/p/luafeiq/">http://code.google.com/p/luafeiq/</a></font></p><img src ="http://www.cppblog.com/kevinlynx/aggbug/139646.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2011-01-31 16:51 <a href="http://www.cppblog.com/kevinlynx/archive/2011/01/31/139646.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>修改tolua++代码支持插入预编译头文件</title><link>http://www.cppblog.com/kevinlynx/archive/2010/02/28/108632.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sun, 28 Feb 2010 12:58:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2010/02/28/108632.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/108632.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2010/02/28/108632.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/108632.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/108632.html</trackback:ping><description><![CDATA[<p><font size="2">tolua++自动生成绑定代码时，不支持插入预编译头文件。虽然可以插入直接的C++代码例如<br>，如$#include xxxx，但插入位置并没有位于文件头。对于使用预编译头的大型工程而言，<br>尤其是某个绑定代码依赖了工程里其他很多东西，更万恶的是预编译头文件里居然包含很多<br>自己写的代码时，支持插入预编译头文件这个功能很重要。 </font> <p><font size="2">说白了，也就是要让tolua++在生成的代码文件开头插入#include "stdafx.h"。 </font> <p><font size="2">修改代码其实很简单。tolua++分析pkg文件及生成代码文件其实都是通过lua代码完成的。<br>在src/bin/lua目录下，或者在源代码里toluabind.c里（把对应的lua代码直接以ASCII码值<br>复制了过来）即为这些代码。 </font> <p><font size="2">首先修改package.lua里的classPackage::preamble函数，可以看出该函数会生成一些代码<br>文件头，模仿着即可写下如下代码： </font> <p><font size="2">if flags['I'] then<br>&nbsp;&nbsp; output( '#include "..flags['I'] )<br>end </font> <p><font size="2">从上下文代码可以看出flags是个全局变量，保存了命令行参数。 </font> <p><font size="2">然后修改tolua.c代码文件，让其往lua环境里传入命令行参数： </font> <p><font size="2">case 'I':setfield(L,t,"I",argv[++i];break; </font> <p><font size="2">本来，这样修改后基本就可以让tolua++支持通过命令行指定是否插入预编译头：<br>tolua++ -o test.cpp -H test.h -I stdafx.h test.pkg </font> <p><font size="2">不过事情并非很顺利，通过开启TOLUA_SCRIPT_RUN宏来让tolua++通过src/bin/lua下的lua<br>代码来完成功能，结果后来发现basic.lua似乎有问题。无奈之下，只好用winhex之类的工<br>具把修改过的package.lua转换为unsigned char B[]置于toluabind.c里，即可正常处理。 </font></p><img src ="http://www.cppblog.com/kevinlynx/aggbug/108632.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2010-02-28 20:58 <a href="http://www.cppblog.com/kevinlynx/archive/2010/02/28/108632.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>lua_yield为什么就必须在return表达式中被调用</title><link>http://www.cppblog.com/kevinlynx/archive/2010/01/17/105883.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Sun, 17 Jan 2010 11:32:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2010/01/17/105883.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/105883.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2010/01/17/105883.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/105883.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/105883.html</trackback:ping><description><![CDATA[<p><font size="2"></font>&nbsp; <p><font size="2">很早前在折腾挂起LUA脚本支持时，接触到lua_yield这个函数。lua manual中给的解释是： </font> <p><font size="2">This function should only be called as the return expression of a C function。 </font> <p><font size="2">而这个函数一般是在一个注册到LUA环境中的C函数里被调用。lua_CFunction要求的原型里<br>，函数的返回值必须返回要返回到LUA脚本中的值的个数。也就是说，在一个不需要挂起的<br>lua_CFunction实现里，也就是一个不需要return lua_yield(...的实现里，我应该return<br>一个返回值个数。 </font> <p><font size="2">但是为什么调用lua_yield就必须放在return表达式里？当时很天真，没去深究，反正发现<br>不按照lua manual里说的做就是不行。而且关键是，lua manual就不告诉你为什么。 </font> <p><font size="2">最近突然就想到这个问题，决定去搞清楚这个问题。侯捷说了，源码面前了无秘密。我甚至<br>在看代码之前，还琢磨着LUA是不是操作了堆栈（系统堆栈）之类的东西。结果随便跟了下<br>代码真的让我很汗颜。有时候人犯傻了真的是一个悲剧。诺简单的一个问题会被人搞得很神<br>秘： </font> <p><font size="2">解释执行调用一个注册进LUA的lua_CFunction是在ldo.c里的luaD_precall函数里，有如下<br>代码： </font> <p><font size="2">&nbsp;&nbsp;&nbsp; n = (*curr_func(L)-&gt;c.f)(L);&nbsp; /* do the actual call */<br>&nbsp;&nbsp;&nbsp; lua_lock(L);<br>&nbsp;&nbsp;&nbsp; if (n &lt; 0)&nbsp; /* yielding? */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return PCRYIELD;<br>&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; luaD_poscall(L, L-&gt;top - n);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return PCRC;<br>&nbsp;&nbsp;&nbsp; } </font> <p><font size="2">多的我就不说了，别人注释写得很清楚了，注册进去的lua_CFunction如果返回值小于0，这<br>个函数就向上层返回PCRYIELD，从名字就可看出是告诉上层需要YIELD。再找到lua_yield函<br>数的实现，恰好该函数就返回-1。 </font> <p><font size="2">要再往上层跟，会到lvm.c里luaV_execute函数，看起来应该就是虚拟机在解释执行指令： </font> <p><font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case OP_CALL: {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int b = GETARG_B(i);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int nresults = GETARG_C(i) - 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (b != 0) L-&gt;top = ra+b;&nbsp; /* else previous instruction set top */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; L-&gt;savedpc = pc;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; switch (luaD_precall(L, ra, nresults)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case PCRLUA: {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nexeccalls++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto reentry;&nbsp; /* restart luaV_execute over new Lua function */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case PCRC: {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* it was a C function (`precall' called it); adjust results */<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (nresults &gt;= 0) L-&gt;top = L-&gt;ci-&gt;top;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; base = L-&gt;base;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue; </font> <p><font size="2">对于PCRYIELD返回值，直接忽略处理了。 </font></p><img src ="http://www.cppblog.com/kevinlynx/aggbug/105883.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2010-01-17 19:32 <a href="http://www.cppblog.com/kevinlynx/archive/2010/01/17/105883.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现自己的LUA绑定器-一个模板编程挑战</title><link>http://www.cppblog.com/kevinlynx/archive/2008/08/13/58684.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Wed, 13 Aug 2008 01:33:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/08/13/58684.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/58684.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/08/13/58684.html#Feedback</comments><slash:comments>12</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/58684.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/58684.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 实现LUA绑定器  author : Kevin Lynx  Preface &nbsp;&nbsp;&nbsp; 当LUA脚本调用我们注册的C函数时，我们需要逐个地从LUA栈里取出调用参数，当函数返回时，又需要一个一个地往LUA栈压入返回值，并且我们注册的函数只能是int()(lua_State*)类型。这很不方便，对于上层程序员来说更不方便。&nbsp;&nbsp;&nbsp; 因此我们要做的...&nbsp;&nbsp;<a href='http://www.cppblog.com/kevinlynx/archive/2008/08/13/58684.html'>阅读全文</a><img src ="http://www.cppblog.com/kevinlynx/aggbug/58684.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-08-13 09:33 <a href="http://www.cppblog.com/kevinlynx/archive/2008/08/13/58684.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现LUA脚本同步处理事件:LUA的coroutine</title><link>http://www.cppblog.com/kevinlynx/archive/2008/08/12/58636.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Tue, 12 Aug 2008 08:02:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2008/08/12/58636.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/58636.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2008/08/12/58636.html#Feedback</comments><slash:comments>12</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/58636.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/58636.html</trackback:ping><description><![CDATA[<p><font size=2>author : Kevin Lynx</font>
<p><font size=2><strong>需求</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 受WOW的影响，LUA越来越多地被应用于游戏中。脚本被用于游戏中主要用于策划编写游戏规则相关。实际运用中，<br>我们会将很多宿主语言函数绑定到LUA脚本中，使脚本可以更多地控制程序运行。例如我们可以绑定NPCDialog之类的函数<br>到LUA中，然后策划便可以在脚本里控制游戏中弹出的NPC对话框。<br>&nbsp;&nbsp;&nbsp; 我们现在面临这样的需求：对于宿主程序而言，某些功能是不能阻塞程序逻辑的（对于游戏程序尤其如此），但是为<br>了方便策划，我们又需要让脚本看起来被阻塞了。用NPCDialog举个例子，在脚本中有如下代码 ：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; ret </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> NPCDialog( </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Hello bitch</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> )<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> ret </span><span style="COLOR: #000000">==</span><span style="COLOR: #000000"> OK then print(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">OK</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">) end</span></div>
<p><br>&nbsp;&nbsp;&nbsp; 对于策划而言，NPCDialog应该是阻塞的，除非玩家操作此对话框，点击OK或者关闭，不然该函数不会返回。而对于<br>宿主程序C++而言，我们如何实现这个函数呢： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> do_npc_dialog( lua_State </span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">L )<br><img id=Codehighlighter1_49_169_Open_Image onclick="this.style.display='none'; Codehighlighter1_49_169_Open_Text.style.display='none'; Codehighlighter1_49_169_Closed_Image.style.display='inline'; Codehighlighter1_49_169_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_49_169_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_49_169_Closed_Text.style.display='none'; Codehighlighter1_49_169_Open_Image.style.display='inline'; Codehighlighter1_49_169_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp; </span><span id=Codehighlighter1_49_169_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_49_169_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">const</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">char</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">content </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> lua_tostring( L, </span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000"> );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua_pushnumber( ret );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp; }</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; 显然，该函数不能阻塞，否则它会阻塞整个游戏线程，这对于服务器而言是不可行的。但是如果该函数立即返回，那<br>么它并没有收集到玩家对于那个对话框的操作。<br>&nbsp;&nbsp;&nbsp; 综上，我们要做的是，让脚本感觉某个操作阻塞，但事实上宿主程序并没有阻塞。 </font>
<p><font size=2><strong>事件机制</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 一个最简单的实现（对于C程序员而言也许也是优美的），就是使用事件机制。我们将对话框的操作结果作为一个事件。<br>脚本里事实上没有哪个函数是阻塞的。为了处理一些&#8220;阻塞&#8221;函数的处理结果，脚本向宿主程序注册事件处理器（同GUI事件<br>处理其实是一样的），例如脚本可以这样：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; function onEvent( ret )<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000"> ret </span><span style="COLOR: #000000">==</span><span style="COLOR: #000000"> OK then print(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">OK</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">) end<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; end<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #000000"> register </span><span style="COLOR: #0000ff">event</span><span style="COLOR: #000000"> handler<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; SetEventHandler( </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">onEvent</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> )<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; NPCDialog(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Hello bitch</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)</span></div>
<p><br>&nbsp;&nbsp;&nbsp; 宿主程序保存事件处理器onEvent函数名，当玩家操作了对话框后，宿主程序回调脚本中的onEvent，完成操作。<br>&nbsp;&nbsp;&nbsp; 事实上我相信有很多人确实是这么做的。这样做其实就是把一个顺序执行的代码流，分成了很多块。但是对于sleep<br>这样的脚本调用呢？例如： </font>
<p><font size=2>&nbsp;</p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #0000ff">do</span><span style="COLOR: #000000"> job A<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; sleep(</span><span style="COLOR: #000000">10</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #0000ff">do</span><span style="COLOR: #000000"> job B<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; sleep(</span><span style="COLOR: #000000">10</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #0000ff">do</span><span style="COLOR: #000000"> job C<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; <img src="http://www.cppblog.com/Images/dot.gif"></span></div>
<p><br>&nbsp;&nbsp;&nbsp; 那么采用事件机制将可能会把代码分解为：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; function onJobA<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #0000ff">do</span><span style="COLOR: #000000"> job A<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetEventHandlerB(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">onJobB</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(</span><span style="COLOR: #000000">10</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; end<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; function onJobB<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #0000ff">do</span><span style="COLOR: #000000"> job B<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetEventHandlerC(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">onJobC</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; end<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; function onJobC<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #0000ff">do</span><span style="COLOR: #000000"> job C<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; end<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #000000">--</span><span style="COLOR: #000000"> script starts here<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; SetEventHandlerA( </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">onJobA</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> )<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; sleep(</span><span style="COLOR: #000000">10</span><span style="COLOR: #000000">)</span></div>
<p><br>&nbsp;&nbsp;&nbsp; 代码看起来似乎有点难看了，最重要的是它不易编写，策划估计会抓狂的。我想，对于非专业程序员而言，程序的<br>顺序执行可能理解起来更为容易。 </font>
<p><font size=2><strong>SOLVE IT</strong> </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 我们的解决方案，其实只有一句话：当脚本执行到阻塞操作时（如NPCDialog），挂起脚本，当宿主程序某个操作完<br>成时，让脚本从之前的挂起点继续执行。<br>&nbsp;&nbsp;&nbsp; 这不是一种假想的功能。我在刚开始实现这个功能之前，以为LUA不支持这个功能。我臆想着如下的操作：<br>&nbsp;&nbsp;&nbsp; 脚本：<br>&nbsp;&nbsp;&nbsp; ret = NPCDialog("Hello bitch")<br>&nbsp;&nbsp;&nbsp; if ret == 0 then print("OK") end<br>&nbsp;&nbsp;&nbsp; 宿主程序：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> do_npc_dialog( lua_State </span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">L )<br><img id=Codehighlighter1_49_112_Open_Image onclick="this.style.display='none'; Codehighlighter1_49_112_Open_Text.style.display='none'; Codehighlighter1_49_112_Closed_Image.style.display='inline'; Codehighlighter1_49_112_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_49_112_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_49_112_Closed_Text.style.display='none'; Codehighlighter1_49_112_Open_Image.style.display='inline'; Codehighlighter1_49_112_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp; </span><span id=Codehighlighter1_49_112_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_49_112_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lua_suspend_script( L );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img src="http://www.cppblog.com/Images/dot.gif"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp; }</span></span><span style="COLOR: #000000"><br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br>&nbsp;&nbsp;&nbsp; 某个地方某个操作完成了：<br>&nbsp;&nbsp;&nbsp; lua_resume_script( L );<br>&nbsp;&nbsp;&nbsp; 当我实现了这个功能后，我猛然发现，实际情况和我这里想的差不多（有点汗颜）。</font></p>
<p><font size=2><br><strong>认识Coroutine</strong> </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; coroutine是LUA中类似线程的东西，但是它其实和fiber更相似。也就是说，它是一种非抢占式的线程，它的切换取决<br>于任务本身，也就是取决你，你决定它们什么时候发生切换。建议你阅读lua manual了解更多。<br>&nbsp;&nbsp;&nbsp; coroutine支持的典型操作有：lua_yield, lua_resume，也就是我们需要的挂起和继续执行。<br>&nbsp;&nbsp;&nbsp; lua_State似乎就是一个coroutine，或者按照LUA文档中的另一种说法，就是一个thread。我这里之所以用&#8217;似乎&#8216;是<br>因为我自己也无法确定，我只能说，lua_State看起来就是一个coroutine。<br>&nbsp;&nbsp;&nbsp; LUA提供lua_newthread用于手工创建一个coroutine，然后将新创建的coroutine放置于堆栈顶，如同其他new出来的<br>对象一样。网上有帖子说lua_newthread创建的东西与脚本里调用coroutine.create创建出来的东西不一样，但是根据我<br>的观察来看，他们是一样的。lua_newthread返回一个lua_State对象，所以从这里可以看出，&#8220;lua_State看起来就是一个<br>coroutine&#8221;。另外，网上也有人说创建新的coroutine代价很大，但是，一个lua_State的代价能有多大？当然，我没做过<br>测试，不敢多言。<br>&nbsp;&nbsp;&nbsp; lua_yield用于挂起一个coroutine，不过该函数只能用于coroutine内部，看看它的参数就知道了。<br>&nbsp;&nbsp;&nbsp; lua_resume用于启动一个coroutine，它可以用于coroutine没有运行时启动之，也可以用于coroutine挂起时重新启动<br>之。lua_resume在两种情况下返回：coroutine挂起或者执行完毕，否则lua_resume不返回。<br>&nbsp;&nbsp;&nbsp; lua_yield和lua_resume对应于脚本函数：coroutine.yield和coroutine.resume，建议你写写脚本程序感受下coroutine，<br>例如：<br></font></p>
<p><font size=2></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; function main()<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">main start</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; coroutine.yield()<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">main end</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; end<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; co</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">coroutine.create( main );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; coroutine.resume(co)<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<p><br><strong>REALLY SOLVE IT</strong> </font></p>
<p><font size=2>&nbsp;&nbsp;&nbsp; 你可能会想到，我们为脚本定义一个main，然后在宿主程序里lua_newthread创建一个coroutine，然后将main放进去，<br>当脚本调用宿主程序的某个&#8217;阻塞&#8216;操作时，宿主程序获取到之前创建的coroutine，然后yield之。当操作完成时，再resume<br>之。<br>&nbsp;&nbsp;&nbsp; 事实上方法是对的，但是没有必要再创建一个coroutine。如之前所说，一个lua_State看上去就是一个coroutine，<br>而恰好，我们始终都会有一个lua_State。感觉上，这个lua_State就像是main coroutine。（就像你的主线程）<br>&nbsp;&nbsp;&nbsp; 思路就是这样，因为具体实现时，还是有些问题，所以我罗列每个步骤的代码。<br>&nbsp;&nbsp;&nbsp; 初始lua_State时如你平时所做：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; lua_State </span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">L </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> lua_open();<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; luaopen_base( L );</span></div>
<p><br>&nbsp;&nbsp;&nbsp; 注册脚本需要的宿主程序函数到L里：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; lua_pushcfunction( L, sleep );<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp; lua_setglobal( L, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">my_sleep</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> );</span></div>
<p><br>&nbsp;&nbsp;&nbsp; 载入脚本文件并执行时稍微有点不同：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; luaL_loadfile( L, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">test.lua</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> );<br><img id=Codehighlighter1_60_73_Open_Image onclick="this.style.display='none'; Codehighlighter1_60_73_Open_Text.style.display='none'; Codehighlighter1_60_73_Closed_Image.style.display='inline'; Codehighlighter1_60_73_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_60_73_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_60_73_Closed_Text.style.display='none'; Codehighlighter1_60_73_Open_Image.style.display='inline'; Codehighlighter1_60_73_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>lua_resume( L, </span><span style="COLOR: #000000">0</span><span style="COLOR: #000000"> ); </span><span id=Codehighlighter1_60_73_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span><span id=Codehighlighter1_60_73_Open_Text><span style="COLOR: #008000">/*</span><span style="COLOR: #008000"> 调用resume </span><span style="COLOR: #008000">*/</span></span></div>
<p><br>&nbsp;&nbsp;&nbsp; 在你的&#8217;阻塞&#8216;函数里需要挂起coroutine：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> lua_yield( L, </span><span style="COLOR: #000000">0</span><span style="COLOR: #000000"> );</span></div>
<p><br>&nbsp;&nbsp;&nbsp; 注意，lua_yield函数非常特别，它必须作为return语句被调用，否则会调用失败，具体原因我也不清楚。而在这里，<br>它作为lua_CFunction的返回值，会不会引发错误？因为lua_CFunction约定返回值为该函数对于脚本而言的返回值个数。<br>实际情况是，我看到的一些例子里都这样安排lua_yield，所以i do what they do。 </font>
<p><font size=2>&nbsp;&nbsp;&nbsp; 在这个操作完成后（如玩家操作了那个对话框），宿主程序需要唤醒coroutine：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp; lua_resume( L, </span><span style="COLOR: #000000">0</span><span style="COLOR: #000000"> ); </span></div>
<p></font>&nbsp;
<p><font size=2>&nbsp;&nbsp;&nbsp; 大致步骤就这些。如果你要单独创建新的lua_State，反而会搞得很麻烦，我开始就是那样的做的，总是实现不了自己<br>预想中的效果。 </font>
<p><font size=2><strong>相关下载：</strong><br>&nbsp;&nbsp;&nbsp; 例子程序中，我给了一个sleep实现。脚本程序调用sleep时将被挂起，宿主程序不断检查当前时间，当时间到时，resume<br>挂起的coroutine。<a href="http://www.cppblog.com/Files/kevinlynx/lua_test_coro.rar" target=_blank>下载例子</a></font></p>
<p><font size=2></font>&nbsp;</p>
<p><font size=2><strong>8.13补充</strong></font></p>
<p><font size=2><strong>&nbsp;</strong>&nbsp; 可能有时候，我们提供给脚本的函数需要返回一些值给脚本，例如NPCDialog返回操作结果，我们只需要在宿主程序里lua_resume</font></p>
<p><font size=2>之前push返回值即可，当然，需要设置lua_resume第二个参数为返回值个数。<br><br><strong>2.9.2010</strong><br>&nbsp;&nbsp;&nbsp; lua_yield( L, nResults )第二个参数指定返回给lua_resume的值个数。如下：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;lua_pushnumber(&nbsp;L,&nbsp;</span><span style="COLOR: #000000">3</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;lua_yield(&nbsp;L,&nbsp;</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;<img src="http://www.cppblog.com/Images/dot.gif"><img src="http://www.cppblog.com/Images/dot.gif"><img src="http://www.cppblog.com/Images/dot.gif"><img src="http://www.cppblog.com/Images/dot.gif">..<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">&nbsp;ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;lua_resume(&nbsp;L,&nbsp;</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(&nbsp;ret&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;LUA_YIELD&nbsp;)<br><img id=Codehighlighter1_134_190_Open_Image onclick="this.style.display='none'; Codehighlighter1_134_190_Open_Text.style.display='none'; Codehighlighter1_134_190_Closed_Image.style.display='inline'; Codehighlighter1_134_190_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_134_190_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_134_190_Closed_Text.style.display='none'; Codehighlighter1_134_190_Open_Image.style.display='inline'; Codehighlighter1_134_190_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top>&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_134_190_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_134_190_Open_Text><span style="COLOR: #000000">{<br><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lua_Number&nbsp;r&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;luaL_checknumber(&nbsp;L,&nbsp;</span><span style="COLOR: #000000">-</span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">&nbsp;);<br><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;}</span></span></div>
</font>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/58636.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-08-12 16:02 <a href="http://www.cppblog.com/kevinlynx/archive/2008/08/12/58636.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>