??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲国产精品一区二区久久hs,久久狠狠一本精品综合网,久久久久四虎国产精品http://www.shnenglu.com/kevinlynx/category/7886.html低调做技术__ C/C++\MMORPG服务器\模块架构__ TODOQlinux env/read more books __Kevin Lynxzh-cnTue, 07 Aug 2012 11:12:20 GMTTue, 07 Aug 2012 11:12:20 GMT60让wxListCtrl支持子item~辑http://www.shnenglu.com/kevinlynx/archive/2012/08/07/186581.htmlKevin LynxKevin LynxTue, 07 Aug 2012 09:09:00 GMThttp://www.shnenglu.com/kevinlynx/archive/2012/08/07/186581.htmlhttp://www.shnenglu.com/kevinlynx/comments/186581.htmlhttp://www.shnenglu.com/kevinlynx/archive/2012/08/07/186581.html#Feedback0http://www.shnenglu.com/kevinlynx/comments/commentRss/186581.htmlhttp://www.shnenglu.com/kevinlynx/services/trackbacks/186581.html

我用的wxLua版本信息?code>wxLua 2.8.7.0 built with wxWidgets 2.8.8Q也是LuaForWindows_v5.1.4-40.exeq个安装包里自带的wxLua。我不知道其他wxWidgets版本里wxListCtrl怎样Q但我用的版本里wxListCtrl是不支持~辑里面的子item的。在我用的report模式下,子item也就是特定某一行一列的item?/p>

google了一下,发现(zhn)剧地需要自己实玎ͼ主要是自己昄一个wxTextCtrlQ?/p>

--
-- file: wxListCtrlTextEdit.lua
-- author: Kevin Lynx
-- date: 08.06.2012
--
local EditList = {}

-- get the column by an abs point
function EditList:getColumn(x)
    local cols = self.listctrl:GetColumnCount()
    local cx = 0
    for i = 0, cols - 1 do
        local w = self.listctrl:GetColumnWidth(i)
        if x <= cx + w then return i end
        cx = cx + w
    end
    return -1
end

-- when a mouse down, show a text edit control 
function EditList:onLeftDown(evt)
    if self.editor:IsShown() then
        self:closeEditor()
    end
    local p = evt:GetPoint()
    local row = evt:GetIndex()
    local col = self:getColumn(p.x)
    local rect = wx.wxListCtrlEx.GetSubItemRect(self.listctrl, row, col)
    rect:SetHeight(rect:GetHeight() + 5) -- adjust
    self.editor:SetSize(rect)
    self.editor:Show()
    self.editor:SetValue(wx.wxListCtrlEx.GetItemText(self.listctrl, row, col))
    self.editor:SetFocus()
    self.col = col
    self.row = row
end

function EditList:closeEditor()
    if not self.editor:IsShown() then return end
    self.editor:Hide()
    self.listctrl:SetItem(self.row, self.col, self.editor:GetValue())
end

function EditList:initialize()
    self.editor = wx.wxTextCtrl(self.listctrl, wx.wxID_ANY, "", wx.wxDefaultPosition, wx.wxDefaultSize, wx.wxTE_PROCESS_ENTER + wx.wxTE_RICH2)
    self.editor:Connect(wx.wxEVT_COMMAND_TEXT_ENTER, function () self:closeEditor() end)
    -- not work actually
    self.editor:Connect(wx.wxEVT_COMMAND_KILL_FOCUS, function () self:closeEditor() end)
    self.editor:Hide()
end

function wx.wxListCtrlTextEdit(listctrl)
    local o = {
        listctrl = listctrl,
        editor = nil,
    }
    local editlist = newObject(o, EditList)
    editlist:initialize()
    listctrl:Connect(wx.wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, function (evt) editlist:onLeftDown(evt) end)
    listctrl:Connect(wx.wxEVT_COMMAND_LIST_ITEM_FOCUSED, function () editlist:closeEditor() end)
    return listctrl
end

其原理就是获取到当前鼠标点击所在的子item位置Q然后在此位|显CZ个wxEditCtrl卛_。以上代码需要依赖我之前写的Lua里实现简单的c?对象中的代码Q同时依赖以下针对wxListCtrl的扩展接口:

--
-- file: wxListCtrlExtend.lua
-- author: Kevin Lynx
-- date: 08.07.2012
-- brief: extend some util functions to wx.wxListCtrl
-- 
wx.wxListCtrlEx = {}

function wx.wxListCtrlEx.GetSubItemRect(listctrl, item, col)
    local rect = wx.wxRect()
    listctrl:GetItemRect(item, rect)
    local x = 0
    local w = 0
    for i = 0, col do
        w = listctrl:GetColumnWidth(i)
        x = x + w
    end
    return wx.wxRect(x - w, rect:GetY(), w, rect:GetHeight())
end

function wx.wxListCtrlEx.GetItemText(listctrl, item, col)
    local info = wx.wxListItem()
    info:SetId(item)
    info:SetColumn(col)
    info:SetMask(wx.wxLIST_MASK_TEXT)
    listctrl:GetItem(info)
    return info:GetText()
end

在我看到的wxWidgets官方文档里,其实wxListCtrl已经?code>GetSubItemRect接口Qƈ且在另一些示例代码里Q也看到?code>GetItemText接口Q但是,我用的版本里没有,所以只好自己写。基于以上,要用这个可以支持编辑子item的wxListCtrlQ可以:

list = wx.wxListCtrlTextEdit(wx.wxListCtrl(dialog, wx.wxID_ANY, wx.wxDefaultPosition, wx.wxDefaultSize, wx.wxLC_REPORT))

也就是通过wx.wxListCtrlTextEditq个函数做下处理Q这个函数返回的是本w的wxListCtrl。当然更好的方式是用承之cȝ方式Q开发一U新的控Ӟ但在Lua中,针对usedatacd的扩展貌似只能这样了?/p>

最好吐槽下Q这个控件扩展其实很恶心。本来我打算当编辑控件失ȝ点后隐藏它Q但是往~辑控g上注册KILL_FOCUS事g始终不v作用Q我又打弄个ESC键盘事gL动取消,但显然wxTextCtrl是不支持键盘事g的。好吧,凑合用了?/p>



Kevin Lynx 2012-08-07 17:09 发表评论
]]>
tolua的tolua_toxxxpdAPI设计http://www.shnenglu.com/kevinlynx/archive/2012/05/10/174460.htmlKevin LynxKevin LynxThu, 10 May 2012 07:38:00 GMThttp://www.shnenglu.com/kevinlynx/archive/2012/05/10/174460.htmlhttp://www.shnenglu.com/kevinlynx/comments/174460.htmlhttp://www.shnenglu.com/kevinlynx/archive/2012/05/10/174460.html#Feedback0http://www.shnenglu.com/kevinlynx/comments/commentRss/174460.htmlhttp://www.shnenglu.com/kevinlynx/services/trackbacks/174460.html原文链接Q?a >http://codemacro.com/2012/05/10/tolua-api/

我们使用tolua++手工l定c/c++接口到l(f)ua中,在绑定的接口实现里,需要取Z入的参数。tolua++中提供了一pdtolua_toxxx函数Q例如:

lua_Number tolua_tonumber(lua_State *L, int narg, lua_Number def)
const char *tolua_tostring(lua_State *L, int narg, const char *def)

q些函数都有一个def参数。乍一看,q些函数使用h很简单。传入lua_StateQ传入参数在栈中的位|,然后再传一?strong>p|后返回的默认倹{?/p>

我重点要说的是这里这?strong>p|Q按正常E序员的理解Q针对lua而言Q什么情况下失败呢Qlua语言里函数参数支持不传,此时实参为nilQ将nil转换Z个ccd必然p|Q参数类型不正确不失败?你传一个user dataQc里按数字来取Q这也算p|?/p>

q么单的APIq需要多U结什么呢Q然后我们浩荡荡地写了上百个接口,什么tolua_tostring/tolua_tonumber的用少说也?00了吧Q?/p>

然后有一天,服务器宕ZQ空指针:

/* p|q回""Q还能省I指针的判断 */
const char *name = tolua_tostring(L, 1, "");
if (name[0] == '\0') { /* IZd判断?*/
 ...
}

跟踪后发玎ͼ脚本里传入的是nilQ这里的name取出来是NULLQ而不?#8221;“Q的地址Q。然后吐槽了一下这个APIQ辛苦地修改了所有类g码,增加对空指针的判断。我没有多想?/p>

故事l箋Q有一天服务器虽然没宕机,但功能不正常?

float angle = (float) tolua_tonumber(L, 1, 2 * PI);
...

q个意思是Q这个函数的参数1默认?*PIQ什么是默认Qlua里某函数参数不传Q或传nil是使用默认。因Z传的话,q个实参本n是nil。但Qtolua_tonumber的行Z是这LQ它的实现真是偷?

TOLUA_API lua_Number tolua_tonumber (lua_State* L, int narg, lua_Number def)
{
 return lua_gettop(L)<abs(narg) ? def : lua_tonumber(L,narg);
}
TOLUA_API const char* tolua_tostring (lua_State* L, int narg, const char* def)
{
 return lua_gettop(L)<abs(narg) ? def : lua_tostring(L,narg);
}

意思是Q只有当你不传的时候,它才q回默认|否则׃llua的API来管Q而luaq些API是不支持应用层的默认参数的,对于lua_tonumber错误时就q回0Qlua_tostring错误时就q回NULL?/p>

q种其行为和其带来的common sense不一致的API设计Q实在让疹{什么是common sense呢?像一个UI库里的按钮,我们都知道有click事gQhover事gQUI库的文档甚至都不需要解释什么是click什么是hoverQ因为大家看到这个东西,有了共识,无需废话Q这是common sense。就像tolua的这些APIQ非常普通,大家一看都期待在意外情况下你能q回def倹{但它竟然不是。实在不行,你可以模仿lua的checkpd函数的实现嘛:

LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) {
 lua_Number d = lua_tonumber(L, narg);
 if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */
 tag_error(L, narg, LUA_TNUMBER);
 return d;
}

卻IҎ(gu)不用L查栈问题Q直接在lua_tonumber之后再做包装查。何况,lua需要你L查栈吗?当你讉K了栈外的元素Ӟlua会自动返回一个全局帔RluaO_nilobject:

static TValue *index2adr(lua_State *L, int idx) {
 ...
 if (o >= L->top) return cast(TValue*, luaO_nilobject);
}

另,E序(zhn)剧也来源于臆想?/p>

Kevin Lynx 2012-05-10 15:38 发表评论
]]>
传递Lua函数到C/C++?/title><link>http://www.shnenglu.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.shnenglu.com/kevinlynx/archive/2011/04/24/144905.html</guid><wfw:comment>http://www.shnenglu.com/kevinlynx/comments/144905.html</wfw:comment><comments>http://www.shnenglu.com/kevinlynx/archive/2011/04/24/144905.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.shnenglu.com/kevinlynx/comments/commentRss/144905.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/kevinlynx/services/trackbacks/144905.html</trackback:ping><description><![CDATA[<div class="l7dv79z" id="luac-c" class="document"><h1 class="title">传递Lua函数到C/C++中</h1><div class="fpr9xv7" id="id1" class="section"><h1>问题</h1><p>在Lua中,因为函数也是第一类值,所以会出现将函数作为另一个函数的参数,或者函数作 为函数的返回值。这种机制在很多地方都能代码更灵活更简洁,例如:</p><pre class="literal-block"> table.sort(table [,comp]) </pre><p>这里的comp就要求传入一个函数,我们在调用时,大概会有如下形式:</p><pre class="literal-block"> table.sort(t, comp) -- 直接写函数名 table.sort(t, local_comp) -- 某个局部函数 table.sort(t, function (a, b) xxx end ) -- 临时构造一个匿名函数 </pre><p>其中最后一种方式最为灵活,任意时候在需要的时候构造一个匿名函数。这种在Lua自身的 环境中使用,自然没有问题。但是,当我们在C/C++中注册一些函数到Lua环境中,而这些 函数也需要使用函数参数的时候,问题就出来了。</p><p>Lua本身是不支持将Lua函数作为函数参数传入C/C++的,不管这个想要传入的函数是全局的 、局部的、或者匿名的(匿名的本质上也算局部的)。一般情况下,我们唯一的交互方式, 不是传入一个函数,而是一个全局函数名。C/C++保存这个函数名,在需要回调Lua的时候, 就在Lua全局表中找到这个函数(根据函数名),然后再调用之。情况大致如下:</p><pre class="literal-block"> function lua_func () xxx end cfunc(lua_func) -- wrong cfunc("lua_func") -- right </pre><p>我们这回的脚本模块,策划会大量使用需要回调函数的C/C++函数。显然,创建大量的全局 函数,先是从写代码的角度看,就是很伤神的。</p></div><div class="fz9z799" id="id2" class="section"><h1>解决</h1><p>我们最终需要的方式,大概如下:</p><pre class="literal-block"> cfunc(lua_func) -- ok cfunc(function () xxx end) -- ok local xxx = function () xxx end cfunc(xxx) -- ok </pre><p>要解决这个问题,我的思路是直接在Lua层做一些包装。因为C/C++那边仅支持传入一个全局 函数名(当然不一定得全局的,根据实际情况,可能在其他自己构造的表里也行),也就是 一个字符串,所以我的思路就是将Lua函数和一个唯一的字符串做映射。:</p><pre class="literal-block"> function wrap (fn) local id = generate_id() local fn_s = "__callback_fn"..id _G[fn_s] = fn return fn_s end </pre><p>这个wrap函数,就是将一个函数在全局表里映射到一个字符串上,那么在使用时:</p><pre class="literal-block"> cfunc(wrap(function () xxx end)) cfunc(const char *fn_name, xxx); -- cfunc的原型 </pre><p>cfunc是C/C++方注册进Lua的函数,它的原型很中规中矩,即:只接收一个函数名,一个字 符串,如之前所说,C/C++要调用这个回调函数时,就根据这个字符串去查找对应的函数。 脚本方在调用时,如果想传入一个匿名函数了,就调用wrap函数包装一下即可。</p></div><div class="97pjtbp" id="id3" class="section"><h1>一个改进</h1><p>上面的方法有个很严重的问题,在多次调用wrap函数后,将导致全局表也随之膨胀。我们需 要想办法在C/C++完成回调后,来清除wrap建立的数据。这个工作当然可以放到C/C++来进行 ,例如每次发生回调后,就设置下全局表。但这明显是不对的,因为违背了接口的设计原则 ,这个额外的机制是在Lua里添加的,那么责任也最好由Lua来负。要解决这个问题,就可以 使用Lua的metamethods机制。这个机制可以在Lua内部发生特定事件时,让应用层得到通知。 这里,我们需要关注__call事件。</p><p>Lua中只要有__call metamethod的值,均可被当作函数调用。例如:</p><pre class="literal-block"> ab(1, 2) </pre><p>这里这个函数调用形式,Lua就会去找ab是否有__call metamethod,如果有,则调用它。这 个事实暗示我们,一个table也可以被调用。一个改进的wrap函数如下:</p><pre class="literal-block"> local function create_callback_table (fn, name) local t = {} t.callback = fn setmetatable (t, {__call = -- 关注__call function (func, ...) -- 在t(xx)时,将调用到这个函数 func.callback (...) -- 真正的回调 del_callback (name) -- 回调完毕,清除wrap建立的数据 end }) return t end function wrap (fn) local id = generate_func_id() -- 产生唯一的id local fn_s = "_callback_fn"..id _G[fn_s] = create_callback_table(fn, fn_s) -- _G[fn_s]对应的是一个表 return fn_s end </pre><p>在我们的C/C++程序中,依然如往常一样,先是从_G里取出函数名对应的对象。虽然这个对 象现在已经是一个table。然后lua_call。</p><p>上面的代码是否会在原有基础上增加不可接受的性能代价?虽然我没有做过实际测试,但是 从表明看来,排除meta table在Lua里的代价,也就多了几次Lua函数调用。</p><p>最后,感叹一下,Lua里的table及metatable机制,实在非常强大。这种强大不是功能堆砌 出来的强大,而是简单东西组合出来的强大。其背后的设计思想,着实让人佩服。</p><p><strong>4.26.2011 Update</strong></p><p>之前的文中说“Lua本身是不支持将Lua函数作为函数参数传入C/C++的“,这句话严格来说不 正确(由某网友评论)。假设函数cfun由c/c++注册,我们是可以编写如下代码的:</p><pre class="literal-block"> cfunc(print) -- 传入Lua函数 </pre><p>但是问题在于,我们无法取出这个函数并保存在c/c++方。Lua提供了一些接口用于取cfunc 的参数,例如luaL_checknumber(封装lua_tonumber)。但没有类似luaL_checkfunction的 接口。Lua中的table有同样的问题。究其原因,主要是Lua中的函数没有直接的c/c++数据结 构对应。</p><p>;; END</p></div></div><img src ="http://www.shnenglu.com/kevinlynx/aggbug/144905.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2011-04-24 17:28 <a href="http://www.shnenglu.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发布http://www.shnenglu.com/kevinlynx/archive/2011/01/31/139646.htmlKevin LynxKevin LynxMon, 31 Jan 2011 08:51:00 GMThttp://www.shnenglu.com/kevinlynx/archive/2011/01/31/139646.htmlhttp://www.shnenglu.com/kevinlynx/comments/139646.htmlhttp://www.shnenglu.com/kevinlynx/archive/2011/01/31/139646.html#Feedback2http://www.shnenglu.com/kevinlynx/comments/commentRss/139646.htmlhttp://www.shnenglu.com/kevinlynx/services/trackbacks/139646.htmll上ơ捣鼓出?a href="http://www.shnenglu.com/kevinlynx/archive/2011/01/23/139187.html" target="_blank">飞秋的群聊协?/a>?鉴于q底没啥事情?qlua写了个简单的协议兼容的IM。本来开始让

另一个同事在iptux的基上修改的Q结果大概是因ؓiptux的代码不是那么容易修改,׃了了之了。这?/font>

刚发布的luafeiq功能非常单,仅支持与飞秋Q包括大部分兼容IP messager的IMQ进行单聊,?/font>消息?/font>

收发Q简易的消息盒子Q暂存未L息)。因为选的库都是跨q_的,所以很Ҏ(gu)的luafeiq也是?/font>q_的,

最主要的是我想在linux下用?/font>

 

之所以选用luaQ一斚w是想l练luaQ另一斚w则是因ؓ开发效率。前D|间在android下写了些java代码Q?/font>

用java写代码觉得甚为爽快(当然不了完)。这几天写了千把行的luaQ也许有3K行,未统计过Q,

感觉也不错。综合来_q些高语言的很多好用的语法Ҏ(gu),例如闭包QclosureQ,垃圾回收Q都提高

了不写代码的速度。当Ӟlua于我而言也算不上完美的语a。例如我l常因ؓ变量敲错字母Q而在q行?/font>

才暴露nil错误。这也许可以通过诸如IDE之类的工具在写代码的时候就l予提示。lua 在遇C个符hQ?/font>

默认地将其处理ؓ全局的。关于这个语法特性早有h提出不爽Q只能说大家设计的准则不一栗(在我?/font>

目里,我直接改写了全局变量的metatableQ从而防止策划随意定义全局变量Q?/font>

 

再来谈谈实现q程中的一些琐事。因为飞U也是IP messager协议的兼容实玎ͼ很多通信除了可以使用

抓包软g分析外,q可以直接通过IP messager的源码来了解。所以,基础通信协议的实现过E也比较

单。飞U与飞秋之间发送私聊消息是l过加密的。其加密q程也不单,更重要的是,我ƈ不想费?/font>

多时间在q上面。后来发现其实可以通过上线消息里某个标志位表明自己不需要加密。这个标志就是消息头

里的option。上U广播出ȝ消息里一旦表明自׃加密Q那么以后和飞秋通信也就不需要解密了?/font>

 

发送私聊消息时Q消息里会携带一个消息ID。这个ID可以通过L法生成Q例如直接取time的倹{接收到

Ҏ(gu)的消息时Q需要取IDQ然后加入回应消息。对Ҏ(gu)到回应消息后Q就知道自己发送成功。这个过E?/font>

是ip messager在UDP上做的消息可靠验证,q程也比较简单?/font>

 

聊消息在之前提到过Q是通过UDP多播实现。我们可以接收所有群的消息。如果之前已l处于某个群里,

那么一旦你上线后(q播上线消息Q,你就可以直接在这个群里发a。但如果你之前不在这个群里,?/font>

可以通过多播一个加入群的消息,然后可以不误来地在这个群里发a。详l的消息值和实现都可以从

luafeiq的代码里dQmessage_sender.luaQ?/font>

 

在linux下接收windows上的飞秋消息Q是需要做字符~码转换的。因为luafeiq使用IUP作ؓUI库,IUP?/font>

linux下用GTK作ؓ底层实现Q默认全部是UTF8~码。luafeiq里我自己写了个lua库,用于~码转换?/font>

 

话说IUP作ؓ一个UI库,q是比较不错的。正如其介绍文档里所_学习曲线低,基本上看一会文档,可?/font>

直接使用了。luafeiq使用的IUP版本臛_需?.0以上。当初在linux下ؓ了安装IUP3.3Q基本花?个小?/font>

旉Q各U奇怪的没多大意义的错误信息。后来换?.2版本Q居然一下子和谐了Q无限怨念?/font>

 

luafeiq目前攑֜googlecode的版本,可以说是一个很不负责Q的版本。早上我才刚把字W编码{换的代码

调试好。今天已l请假,安׃台电(sh)脑,也就试不了q个字符~码转换是否真的能正常工作。我?/font>

windows下dump了些字符Q看上去能正常功能。明天得回老家q春节,上不了网Q烦性就提前发布了?/font>

 

luafeiq目地址Q?a title="http://code.google.com/p/luafeiq/" >http://code.google.com/p/luafeiq/



Kevin Lynx 2011-01-31 16:51 发表评论
]]>
修改tolua++代码支持插入预编译头文ghttp://www.shnenglu.com/kevinlynx/archive/2010/02/28/108632.htmlKevin LynxKevin LynxSun, 28 Feb 2010 12:58:00 GMThttp://www.shnenglu.com/kevinlynx/archive/2010/02/28/108632.htmlhttp://www.shnenglu.com/kevinlynx/comments/108632.htmlhttp://www.shnenglu.com/kevinlynx/archive/2010/02/28/108632.html#Feedback0http://www.shnenglu.com/kevinlynx/comments/commentRss/108632.htmlhttp://www.shnenglu.com/kevinlynx/services/trackbacks/108632.htmltolua++自动生成l定代码Ӟ不支持插入预~译头文件。虽然可以插入直接的C++代码例如
Q如$#include xxxxQ但插入位置q没有位于文件头。对于用预~译头的大型工程而言Q?br>其是某个绑定代码依赖了工程里其他很多东西,更万恶的是预~译头文仉居然包含很多
自己写的代码Ӟ支持插入预编译头文gq个功能很重要?

说白了,也就是要让tolua++在生成的代码文g开头插?include "stdafx.h"?

修改代码其实很简单。tolua++分析pkg文g及生成代码文件其实都是通过lua代码完成的?br>在src/bin/lua目录下,或者在源代码里toluabind.c里(把对应的lua代码直接以ASCII码?br>复制了过来)即ؓq些代码?

首先修改package.lua里的classPackage::preamble函数Q可以看函数会生成一些代?br>文g_模仿着卛_写下如下代码Q?

if flags['I'] then
   output( '#include "..flags['I'] )
end

从上下文代码可以看出flags是个全局变量Q保存了命o行参数?

然后修改tolua.c代码文gQ让其往lua环境里传入命令行参数Q?

case 'I':setfield(L,t,"I",argv[++i];break;

本来Q这样修改后基本可以让tolua++支持通过命o行指定是否插入预~译_
tolua++ -o test.cpp -H test.h -I stdafx.h test.pkg

不过事情q很顺利,通过开启TOLUA_SCRIPT_RUN宏来让tolua++通过src/bin/lua下的lua
代码来完成功能,l果后来发现basic.luag有问题。无奈之下,只好用winhex之类的工
h修改q的package.lua转换为unsigned char B[]|于toluabind.c里,卛_正常处理?



Kevin Lynx 2010-02-28 20:58 发表评论
]]>
lua_yieldZ么就必须在return表达式中被调?/title><link>http://www.shnenglu.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.shnenglu.com/kevinlynx/archive/2010/01/17/105883.html</guid><wfw:comment>http://www.shnenglu.com/kevinlynx/comments/105883.html</wfw:comment><comments>http://www.shnenglu.com/kevinlynx/archive/2010/01/17/105883.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/kevinlynx/comments/commentRss/105883.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/kevinlynx/services/trackbacks/105883.html</trackback:ping><description><![CDATA[<p><font size="2"></font>  <p><font size="2">很早前在折腾挂vLUA脚本支持Ӟ接触到l(f)ua_yieldq个函数。lua manual中给的解释是Q?</font> <p><font size="2">This function should only be called as the return expression of a C function?</font> <p><font size="2">而这个函C般是在一个注册到LUA环境中的C函数里被调用。lua_CFunction要求的原型里<br>Q函数的q回值必返回要q回到LUA脚本中的值的个数。也是_在一个不需要挂L<br>lua_CFunction实现里,也就是一个不需要return lua_yield(...的实现里Q我应该return<br>一个返回g数?</font> <p><font size="2">但是Z么调用lua_yield必L在return表达式里Q当时很天真Q没LIӞ反正发现<br>不按照lua manual里说的做是不行。而且关键是,lua manual׃告诉你ؓ什么?</font> <p><font size="2">最q突然就惛_q个问题Q决定去搞清楚这个问题。侯捯了,源码面前了无U密。我甚至<br>在看代码之前Q还琢磨着LUA是不是操作了堆栈Q系l堆栈)之类的东ѝ结果随便跟了下<br>代码真的让我很汗颜。有时候h犯傻了真的是一个?zhn)剧。诺单的一个问题会被h搞得很神<br>U: </font> <p><font size="2">解释执行调用一个注册进LUA的lua_CFunction是在ldo.c里的luaD_precall函数里,有如?br>代码Q?</font> <p><font size="2">    n = (*curr_func(L)->c.f)(L);  /* do the actual call */<br>    lua_lock(L);<br>    if (n < 0)  /* yielding? */<br>      return PCRYIELD;<br>    else {<br>      luaD_poscall(L, L->top - n);<br>      return PCRC;<br>    } </font> <p><font size="2">多的我就不说了,别h注释写得很清楚了Q注册进ȝlua_CFunction如果q回值小?Q这<br>个函数就向上层返回PCRYIELDQ从名字可看出是告诉上层需要YIELD。再扑ֈl(f)ua_yield?br>数的实现Q恰好该函数p?1?</font> <p><font size="2">要再往上层跟,会到l(f)vm.c里luaV_execute函数Q看h应该是虚拟机在解释执行指oQ?</font> <p><font size="2">      case OP_CALL: {<br>        int b = GETARG_B(i);<br>        int nresults = GETARG_C(i) - 1;<br>        if (b != 0) L->top = ra+b;  /* else previous instruction set top */<br>        L->savedpc = pc;<br>        switch (luaD_precall(L, ra, nresults)) {<br>          case PCRLUA: {<br>            nexeccalls++;<br>            goto reentry;  /* restart luaV_execute over new Lua function */<br>          }<br>          case PCRC: {<br>            /* it was a C function (`precall' called it); adjust results */<br>            if (nresults >= 0) L->top = L->ci->top;<br>            base = L->base;<br>            continue; </font> <p><font size="2">对于PCRYIELDq回|直接忽略处理了?</font></p><img src ="http://www.shnenglu.com/kevinlynx/aggbug/105883.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2010-01-17 19:32 <a href="http://www.shnenglu.com/kevinlynx/archive/2010/01/17/105883.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现自己的LUAl定?一个模板编E挑?/title><link>http://www.shnenglu.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.shnenglu.com/kevinlynx/archive/2008/08/13/58684.html</guid><wfw:comment>http://www.shnenglu.com/kevinlynx/comments/58684.html</wfw:comment><comments>http://www.shnenglu.com/kevinlynx/archive/2008/08/13/58684.html#Feedback</comments><slash:comments>12</slash:comments><wfw:commentRss>http://www.shnenglu.com/kevinlynx/comments/commentRss/58684.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/kevinlynx/services/trackbacks/58684.html</trackback:ping><description><![CDATA[     摘要: 实现LUAl定? author : Kevin Lynx Preface     当LUA脚本调用我们注册的C函数Ӟ我们需要逐个CLUA栈里取出调用参数Q当函数q回Ӟ又需要一个一个地往LUA栈压入返回|q且我们注册的函数只能是int()(lua_State*)cd。这很不方便Q对于上层程序员来说更不方便?nbsp;   因此我们要做?..  <a href='http://www.shnenglu.com/kevinlynx/archive/2008/08/13/58684.html'>阅读全文</a><img src ="http://www.shnenglu.com/kevinlynx/aggbug/58684.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2008-08-13 09:33 <a href="http://www.shnenglu.com/kevinlynx/archive/2008/08/13/58684.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实现LUA脚本同步处理事g:LUA的coroutinehttp://www.shnenglu.com/kevinlynx/archive/2008/08/12/58636.htmlKevin LynxKevin LynxTue, 12 Aug 2008 08:02:00 GMThttp://www.shnenglu.com/kevinlynx/archive/2008/08/12/58636.htmlhttp://www.shnenglu.com/kevinlynx/comments/58636.htmlhttp://www.shnenglu.com/kevinlynx/archive/2008/08/12/58636.html#Feedback12http://www.shnenglu.com/kevinlynx/comments/commentRss/58636.htmlhttp://www.shnenglu.com/kevinlynx/services/trackbacks/58636.htmlauthor : Kevin Lynx

需?/strong>

    受WOW的媄响,LUA来多地被应用于游戏中。脚本被用于游戏中主要用于策划编写游戏规则相兟뀂实际运用中Q?br>我们会将很多宿主语言函数l定到LUA脚本中,使脚本可以更多地控制E序q行。例如我们可以绑定NPCDialog之类的函?br>到LUA中,然后{划便可以在脚本里控制游戏中弹出的NPC对话框?br>    我们现在面͘q样的需求:对于宿主E序而言Q某些功能是不能dE序逻辑的(对于游戏E序其如此Q,但是?br>了方便策划,我们又需要让脚本看v来被d了。用NPCDialog举个例子Q在脚本中有如下代码 Q?br>

    ret = NPCDialog( "Hello bitch" )
   
if ret == OK then print("OK") end


    对于{划而言QNPCDialog应该是阻塞的Q除非玩家操作此对话框,点击OK或者关闭,不然该函C会返回。而对?br>宿主E序C++而言Q我们如何实现这个函数呢Q?

 

    static int do_npc_dialog( lua_State *L )
   
{
       
const char *content = lua_tostring( L, -1 );
       
        lua_pushnumber( ret );
       
return 1;
    }


    昄Q该函数不能dQ否则它会阻塞整个游戏线E,q对于服务器而言是不可行的。但是如果该函数立即q回Q那
么它q没有收集到玩家对于那个对话框的操作?br>    lgQ我们要做的是,让脚本感觉某个操作阻塞,但事实上宿主E序q没有阻塞?

事g机制

    一个最单的实现Q对于CE序员而言也许也是优美的)Q就是用事件机制。我们将对话框的操作l果作ؓ一个事件?br>脚本里事实上没有哪个函数是阻塞的。ؓ了处理一?#8220;d”函数的处理结果,脚本向宿ȝ序注册事件处理器Q同GUI事g
处理其实是一LQ,例如脚本可以q样Q?br>

    function onEvent( ret )
       
if ret == OK then print("OK") end
    end
   
-- register event handler
    SetEventHandler(
"onEvent" )
    NPCDialog(
"Hello bitch")


    宿主E序保存事g处理器onEvent函数名,当玩家操作了对话框后Q宿ȝ序回调脚本中的onEventQ完成操作?br>    事实上我怿有很多h实是这么做的。这样做其实是把一个顺序执行的代码,分成了很多块。但是对于sleep
q样的脚本调用呢Q例如:

 

    --do job A
    sleep(
10)
   
--do job B
    sleep(
10)
   
--do job C
   


    那么采用事g机制可能会把代码分解ؓQ?br>

    function onJobA
       
--do job A
        SetEventHandlerB(
"onJobB")
        sleep(
10)
    end
    function onJobB
       
--do job B
        SetEventHandlerC(
"onJobC")
    end
    function onJobC
       
--do job C
    end
   
-- script starts here
    SetEventHandlerA(
"onJobA" )
    sleep(
10)


    代码看v来似乎有炚w看了Q最重要的是它不易编写,{划估计会抓狂的。我惻I对于非专业程序员而言Q程序的
序执行可能理解h更ؓҎ(gu)?

SOLVE IT

    我们的解x案,其实只有一句话Q当脚本执行到阻塞操作时Q如NPCDialogQ,挂v脚本Q当宿主E序某个操作?br>成时Q让脚本从之前的挂v点l执行?br>    q不是一U假想的功能。我在刚开始实现这个功能之前,以ؓLUA不支持这个功能。我臆想着如下的操作:
    脚本Q?br>    ret = NPCDialog("Hello bitch")
    if ret == 0 then print("OK") end
    宿主E序Q?br>

    static int do_npc_dialog( lua_State *L )
   
{
       
        lua_suspend_script( L );
       
    }


    某个地方某个操作完成了:
    lua_resume_script( L );
    当我实现了这个功能后Q我猛然发现Q实际情况和我这里想的差不多Q有Ҏ(gu)颜)?/font>


认识Coroutine

    coroutine是LUA中类似线E的东西Q但是它其实和fiber更相伹{也是_它是一U非抢占式的U程Q它的切换取?br>于Q务本w,也就是取决你Q你军_它们什么时候发生切换。徏议你阅读lua manual了解更多?br>    coroutine支持的典型操作有Qlua_yield, lua_resumeQ也是我们需要的挂v和l执行?br>    lua_Stateg是一个coroutineQ或者按照LUA文档中的另一U说法,是一个thread。我q里之所以用’g‘?br>因ؓ我自׃无法定Q我只能_lua_State看v来就是一个coroutine?br>    LUA提供lua_newthread用于手工创徏一个coroutineQ然后将新创建的coroutine攄于堆栈顶Q如同其他new出来?br>对象一栗网上有帖子说lua_newthread创徏的东西与脚本里调用coroutine.create创徏出来的东西不一P但是Ҏ(gu)?br>的观察来看,他们是一L。lua_newthreadq回一个lua_State对象Q所以从q里可以看出Q?#8220;lua_State看v来就是一?br>coroutine”。另外,|上也有创徏新的coroutine代h(hun)很大Q但是,一个lua_State的代仯有多大?当然Q我没做q?br>试Q不敢多a?br>    lua_yield用于挂v一个coroutineQ不q该函数只能用于coroutine内部Q看看它的参数就知道了?br>    lua_resume用于启动一个coroutineQ它可以用于coroutine没有q行时启动之Q也可以用于coroutine挂v旉新启?br>之。lua_resume在两U情况下q回Qcoroutine挂v或者执行完毕,否则lua_resume不返回?br>    lua_yield和lua_resume对应于脚本函敎ͼcoroutine.yield和coroutine.resumeQ徏议你写写脚本E序感受下coroutineQ?br>例如Q?br>

    function main()
        print(
"main start")
        coroutine.yield()
        print(
"main end")
    end
    co
=coroutine.create( main );
    coroutine.resume(co)


REALLY SOLVE IT

    你可能会惛_Q我们ؓ脚本定义一个mainQ然后在宿主E序里lua_newthread创徏一个coroutineQ然后将main放进去,
当脚本调用宿ȝ序的某个’d‘操作Ӟ宿主E序获取C前创建的coroutineQ然后yield之。当操作完成Ӟ再resume
之?br>    事实上方法是对的Q但是没有必要再创徏一个coroutine。如之前所_一个lua_State看上d是一个coroutineQ?br>而恰好,我们始终都会有一个lua_State。感觉上Q这个lua_State像是main coroutine。(像你的ȝE)
    思\是q样Q因为具体实现时Q还是有些问题,所以我|列每个步骤的代码?br>    初始lua_State时如你^时所做:

    lua_State *L = lua_open();
    luaopen_base( L );


    注册脚本需要的宿主E序函数到L里:

    lua_pushcfunction( L, sleep );
    lua_setglobal( L,
"my_sleep" );


    载入脚本文gq执行时E微有点不同Q?br>

    luaL_loadfile( L, "test.lua" );
lua_resume( L,
0 ); /* 调用resume */


    在你?#8217;d‘函数里需要挂起coroutineQ?br>

    return lua_yield( L, 0 );


    注意Qlua_yield函数非常特别Q它必须作ؓreturn语句被调用,否则会调用失败,具体原因我也不清楚。而在q里Q?br>它作为lua_CFunction的返回|会不会引发错误?因ؓlua_CFunctionU定q回gؓ该函数对于脚本而言的返回g数?br>实际情况是,我看到的一些例子里都这样安排lua_yieldQ所以i do what they do?

    在这个操作完成后Q如玩家操作了那个对话框Q,宿主E序需要唤醒coroutineQ?br>

    lua_resume( L, 0 );

 

    大致步骤p些。如果你要单独创建新的lua_StateQ反而会搞得很麻烦,我开始就是那L做的QL实现不了自己
预想中的效果?

相关下蝲Q?/strong>
    例子E序中,我给了一个sleep实现。脚本程序调用sleep时将被挂P宿主E序不断查当前时_当时间到Ӟresume
挂v的coroutine?a href="http://www.shnenglu.com/Files/kevinlynx/lua_test_coro.rar" target=_blank>下蝲例子

 

8.13补充

   可能有时候,我们提供l脚本的函数需要返回一些值给脚本Q例如NPCDialogq回操作l果Q我们只需要在宿主E序里lua_resume

之前pushq回值即可,当然Q需要设|lua_resumeW二个参Cؓq回g数?br>
2.9.2010
    lua_yield( L, nResults )W二个参数指定返回给lua_resume的g数。如下:

   lua_pushnumber( L, 3 );
   
return lua_yield( L, 1 );
 ..
   
int ret = lua_resume( L, 0 );
   
if( ret == LUA_YIELD )
   
{
         lua_Number r 
= luaL_checknumber( L, -1 );
   }


Kevin Lynx 2008-08-12 16:02 发表评论
]]>
޾Ʒ˾þþ| þҹ³˿Ƭ| AƬٸþ| þۺϾþ߾Ʒ | 999þþƷ| þþþùƷ| þþƷAV| 99þù޸ۿ2024| þֻǾƷ23| 91þ㽶Ů߿| þþþĻɫ| þþƷĻþ| þþƷŷƬ| þþþ㽶Ƶ| ŷþһ| ޺ݺۺϾþѿ| þùӰԺ| þþƷ| ۺ˾þôý| þþžѸƵ| ޹ƷȾþ| Ʒþþþþר| þþþavۺϲҰ| 9391ƷۺϾþ㽶| 999þþѾƷ| ҹþƷþþþ| 7777Ʒ˾þþô߽| ŷ龫Ʒþþþþþ| 88þþƷһëƬ | þۺ³³| þþƷ99Ʒ| þþþþúݺݶ| ھƷþù½| 99þ뾫Ʒϵ | 99reþùƷҳ| ޾Ʒھþ| þۺϾƷþ| þþƷaĻ þþƷaĻؿ | þ99Ʒһ| þùƵ| պŮ18վþþƷ|