Lua


元表

Mode 描述
__add
__sub
__mul
__div
__mod 取余
__unm 取相反数
__concat 连接
__eq 等于
__lt 小于
__le 小于等于
__call 将table作为方法调用时会调用元表的__call
__tostring 将table作为string传入时会调用元表的__tostring
__index 访问一个table的字段时,如果table有这个字段,直接返回对应的值;当table没有这个字段,就会去找table的元表的index,没有找到index返回nil。如果有的话分两种情况:__index可以是方法,也可以是一个table。
__newindex 给table的key赋值时(注意原先没有改key的值),调用此方法。

实例:

-- 数据追踪

local DataTracker = {};

DataTracker._t = {};

function DataTracker:trackingDataModify(obj,trackers,isAssert)

    --未传obj或trackers,直接return,不执行以下逻辑
    if (not obj) or (not trackers) then
        return;
    end

    --判断是否需要设置元表
    local isSetMetatable = false;
    if not obj._trackers then
        obj._trackers = {};
        isSetMetatable = true;
    end
    --保存新增监控键值
    for _,tv in pairs(trackers) do
        self._t[obj] = self._t[obj] or {};
        if not self._t[obj][tv] then
            self._t[obj][tv] = {_times = 1, _isAssert = isAssert};
        else
            self._t[obj][tv]._isAssert = isAssert;
        end
        if not obj._trackers[tv] then
            obj._trackers[tv] = obj[tv];
        end
        obj[tv] = nil;
    end

    if isSetMetatable then
        --设置元表
        local mt = {
            __index = function(t,k)
                if t._trackers and t._trackers[tv] then
                    return t._trackers[tv];
                else
                    return rawget(t,k);
                end
            end,
            __newindex = function(t,k,v)
                if self._t[obj] and self._t[obj][k] and self._t[obj][k]._times then
                    if not self._t[obj][k]._isAssert then
                        print(string.format("第%d次修改键值为%s的字段,修改前值为:%s,修改后值为:%s。%s\n",
                            self._t[obj][k]._times,
                            tostring(k),
                            tostring(t._trackers[k]), 
                            tostring(v),
                            debug.traceback("",2)
                        ));
                        self._t[obj][k]._times = self._t[obj][k]._times + 1;
                        t._trackers[k] = v;
                    else
                        assert(false,"第"..tostring(self._t[obj][k]._times or 1).."次修改键值为“"..tostring(k).."”的字段,此次你没权限修改!!");
                    end
                else
                    if t._trackers and t._trackers[k] then
                        t._trackers[k]= nil;
                    end
                    rawset(t,k,v);
                end
            end
        };
        setmetatable(obj, mt);
    end
end

return DataTracker;

协程

方法 描述
coroutine.create() 创建 coroutine,返回 coroutine, 参数是一个函数,当和 resume 配合使用的时候就唤醒函数调用
coroutine.resume() 重启 coroutine,和 create 配合使用
coroutine.yield() 挂起 coroutine,将 coroutine 设置为挂起状态,这个和 resume 配合使用能有很多有用的效果
coroutine.status() 查看 coroutine 的状态。注意:coroutine 的状态有三种:dead,suspended,running,具体什么时候有这样的状态请参考下面的程序
coroutine.wrap() 创建 coroutine,返回一个函数,一旦你调用这个函数,就进入 coroutine,和 create 功能重复
coroutine.running() 返回正在跑的 coroutine,一个 coroutine 就是一个线程,当使用running的时候,就是返回一个 corouting 的线程号

实际使用:

-- tasklet.lua

local M = {}

local function resume_task(task, ...)
    if task.paused then
        task.pending_args = {...}
        return
    end
    local success
    success, task.action = coroutine.resume(task.coroutine, ...)
    if not success then
        local msg = debug.traceback(task.coroutine, task.action)
        print(msg)
    elseif task.action then
        task.action(function(...)
            resume_task(task, ...)
        end)
        return
    end
end

---
-- 启动微线程。
-- @function [parent=#tasklet] spawn
-- @param #function fn
-- @param ... 传给fn的参数。
-- @usage
-- tasklet.spawn(function(arg1, arg2)
--     while true do
--         local dt = tasklet.sleep(0.1)
--         sprite.x = sprite.x + dt * 10
--     end
-- end, arg1, arg2)
function M.spawn(fn, ...)
    local co = coroutine.create(fn)
    local task = {
        coroutine = co,
    }
    resume_task(task, ...)
    return task
end
---
-- 停止微线程。
-- @function [parent=#tasklet] cancel
function M.cancel(task)
    if not task.action then
        return 'invalid task status'
    end
    if type(task.action) == 'table' and task.action.cancel then
        task.action:cancel()
    end
    task.paused = true
    task.action = nil
end

---
-- 暂停微线程。
-- @function [parent=#tasklet] pause
function M.pause(task)
    assert(task.action, 'invalid task status')
    if type(task.action) == 'table' and task.action.pause then
        task.action:pause()
    else
        task.paused = true
    end
end

---
-- 恢复微线程。
-- @function [parent=#tasklet] resume
function M.resume(task)
    assert(task.action, 'invalid task status')
    if type(task.action) == 'table' and task.action.resume then
        task.action:resume()
    else
        resume_task(task, unpack(task.pending_args))
    end
end

---
-- 在微线程中执行,暂停 n 秒
-- @function [parent=#tasklet] sleep
-- @param #number n
function M.sleep(n)
    -- sleep action
    return coroutine.yield(setmetatable({
        _handler = nil,
        cancel = function(self)
            if self._handler then
                self._handler:cancel() -- 取消定时器
            end
        end,
        pause = function(self)
            if self._handler then
                self._handler.paused = true -- 暂停定时器
                return true
            end
            return false
        end,
        resume = function(self)
            if self._handler then
                self._handler.paused = false -- 恢复定时器
                return true
            end
            return false
        end,
    }, {
        __call = function(self, callback)
            self._handler = schedule_once(callback, n) -- n毫秒后执行一次callback,返回定时器句柄
        end
    }))
end

return M

Lua和C++

Lua和C++语言通信的主要方法是:一个无处不在的虚拟栈(先进后出)。

lua提供了C API对栈进行操作

#include <iostream>
#include <string.h>
using namespace std;

extern "C" {
    #include "lua.h"
    #include "lauxlib.h"
    #include "lualib.h"
}
void main() {
    //1.创建一个state
    lua_State *L = luaL_newstate();

    //2.入栈操作
    lua_pushstring(L, "I am so cool~"); 
    lua_pushnumber(L,20);

    //3.取值操作
    if( lua_isstring(L,1)){                //判断是否可以转为string
        cout<<lua_tostring(L,1)<<endl;    //转为string并返回
    }
    if( lua_isnumber(L,2)){
        cout<<lua_tonumber(L,2)<<endl;
    }

    //4.关闭state
    lua_close(L);
    return ;
}

c++调用lua

堆栈操作是基于栈顶的,就是说它只会去操作栈顶的值。

函数调用流程是先将函数入栈,参数入栈,然后用lua_pcall调用函数,此时栈顶为参数,栈底为函数,所以栈过程大致会是:参数出栈->保存参数->参数出栈->保存参数->函数出栈->调用函数->返回结果入栈。

类似的还有lua_setfield,设置一个表的值,肯定要先将值出栈,保存,再去找表的位置。

lua调用c++

1. 直接将模块写入lua源码中

将函数写lua.c中,然后重新编译lua文件。

2. 使用静态依赖的方式

在c++中写一个模块函数,将函数注册到lua解释器中,然后由c++去执行我们的lua文件,然后在lua中调用刚刚注册的函数。

3. 使用dll动态链接的方式

新建一个dll工程,然后编写c++模块。

在Lua中通过require "dllName"来使用。 Lua会进行以下操作:

local path = "dllName.dll"
local f = package.loadlib(path,"luaopen_dllName") -- -- 返回luaopen_dllName函数
f() -- 执行函数

总结

  • c++调用lua实际上是:由c++先把数据放入栈中,由lua去栈中取数据,然后返回数据对应的值到栈顶,再由栈顶返回c++。
  • lua调c++也一样:先编写自己的c模块,然后注册函数到lua解释器中,然后由lua去调用这个模块的函数。

results matching ""

    No results matching ""