这里提供
Lua
中实现 OO 的一种方案:
local _class={}
function class(super)
local class_type={}
class_type.ctor=false
class_type.super=super
class_type.new=function(...)
local obj={}
do
local create
create = function(c,...)
if c.super then
create(c.super,...)
end
if c.ctor then
c.ctor(obj,...)
end
end
create(class_type,...)
end
setmetatable(obj,{ __index=_class[class_type] })
return obj
end
local vtbl={}
_class[class_type]=vtbl
setmetatable(class_type,{__newindex=
function(t,k,v)
vtbl[k]=v
end
})
if super then
setmetatable(vtbl,{__index=
function(t,k)
local ret=_class[super][k]
vtbl[k]=ret
return ret
end
})
end
return class_type
end
现在,我们来看看怎么使用:
base_type=class() -- 定义一个基类 base_type
function base_type:ctor(x) -- 定义 base_type 的构造函数
print("base_type ctor")
self.x=x
end
function base_type:print_x() -- 定义一个成员函数 base_type:print_x
print(self.x)
end
function base_type:hello() -- 定义另一个成员函数 base_type:hello
print("hello base_type")
end
以上是基本的 class 定义的语法,完全兼容 lua 的编程习惯。我增加了一个叫做 ctor 的词,作为构造函数的名字。
下面看看怎样继承:
test=class(base_type) -- 定义一个类 test 继承于 base_type
function test:ctor() -- 定义 test 的构造函数
print("test ctor")
end
function test:hello() -- 重载 base_type:hello 为 test:hello
print("hello test")
end
现在可以试一下了:
a=test.new(1) -- 输出两行,base_type ctor 和 test ctor 。这个对象被正确的构造了。
a:print_x() -- 输出 1 ,这个是基类 base_type 中的成员函数。
a:hello() -- 输出 hello test ,这个函数被重载了。
lua md5库返回32个字符的:
https://github.com/kikito/md5.lua
local _class={} function class(super) local class_type={} class_type.ctor=false class_type.super=super class_type.new=function(...) local obj={} do local create create = function(c,...) if c.super then create(c.super,...) end if c.ctor then c.ctor(obj,...) end end create(class_type,...) end setmetatable(obj,{ __index=_class[class_type] }) return obj end local vtbl={} _class[class_type]=vtbl setmetatable(class_type,{__newindex= function(t,k,v) vtbl[k]=v end }) if super then setmetatable(vtbl,{__index= function(t,k) local ret=_class[super][k] vtbl[k]=ret return ret end }) end return class_type end
现在,我们来看看怎么使用:
base_type=class() -- 定义一个基类 base_type function base_type:ctor(x) -- 定义 base_type 的构造函数 print("base_type ctor") self.x=x end function base_type:print_x() -- 定义一个成员函数 base_type:print_x print(self.x) end function base_type:hello() -- 定义另一个成员函数 base_type:hello print("hello base_type") end
以上是基本的 class 定义的语法,完全兼容 lua 的编程习惯。我增加了一个叫做 ctor 的词,作为构造函数的名字。
下面看看怎样继承:
test=class(base_type) -- 定义一个类 test 继承于 base_type function test:ctor() -- 定义 test 的构造函数 print("test ctor") end function test:hello() -- 重载 base_type:hello 为 test:hello print("hello test") end
现在可以试一下了:
a=test.new(1) -- 输出两行,base_type ctor 和 test ctor 。这个对象被正确的构造了。 a:print_x() -- 输出 1 ,这个是基类 base_type 中的成员函数。 a:hello() -- 输出 hello test ,这个函数被重载了。
lua md5库返回32个字符的:
序列化Lua表
这里是一个简单的 Lua table 序列化函数,只支持 number 或 string 做 key ,但是 value 可以是一个 table ,并支持循环引用:
function serialize(t) local mark={} local assign={} local function ser_table(tbl,parent) mark[tbl]=parent local tmp={} for k,v in pairs(tbl) do local key= type(k)=="number" and "["..k.."]" or k if type(v)=="table" then local dotkey= parent..(type(k)=="number" and key or "."..key) if mark[v] then table.insert(assign,dotkey.."="..mark[v]) else table.insert(tmp, key.."="..ser_table(v,dotkey)) end else table.insert(tmp, key.."="..v) end end return "{"..table.concat(tmp,",").."}" end return "do local ret="..ser_table(t,"ret")..table.concat(assign," ").." return ret end" end t = { a = 1, b = 2,} g = { c = 3, d = 4, t} t.rt = g print(serialize(t))
其输出将是这样的
do local ret={a=1,rt={c=3,d=4},b=2}ret.rt[1]=ret return ret end
为Lua扩展一个循环队列
这里展示用 lua 的 cclosure 扩展的一个技巧,实现了一个 lua 的循环队列:
参考:
lua cclosure 的 upvalue 数量限制
参考:
![在新窗口中打开 [InterWiki]](https://i-blog.csdnimg.cn/blog_migrate/9eefb2c2648cb6095e923220c199df61.gif)
struct queue_data { int size; int head; int tail; }; /* 当传入一个非 nil 的 lua 对象时,这个对象将进入队列。返回 true 表示成功,否则队列满 当不传参数时,把队首元素出队列并返回出去;返回空表示队列空 */ static int queue(lua_State *L) { struct queue_data *qd=(struct queue_data*)g_api->lua_touserdata(L,lua_upvalueindex(1)); if (g_api->lua_gettop(L)==0) { // queue leave if (qd->head==qd->tail) { return 0; } g_api->lua_pushvalue(L,lua_upvalueindex(qd->head)); g_api->lua_pushnil(L); g_api->lua_replace(L,lua_upvalueindex(qd->head)); ++qd->head; if (qd->head > qd->size) { qd->head=2; } return 1; } else { // queue enter int tail=qd->tail+1; if (tail>qd->size) { tail=2; } if (tail==qd->head) { // queue overflow return 0; } g_api->lua_settop(L,1); g_api->lua_replace(L,lua_upvalueindex(qd->tail)); qd->tail=tail; g_api->lua_pushboolean(L,true); return 1; } } /* 创建一个尺寸为 size 的队列对象并返回,size 收到 upvalue 数量限制,这里最大可以为 253 */ int queue_create(lua_State *L) { struct queue_data *qd; g_api->lua_settop(L,1); int size=g_api->lua_tointeger(L,1)+2; if (size>253 || !g_api->lua_checkstack(L,size)) { return 0; } g_api->lua_settop(L,0); qd=(struct queue_data*)g_api->lua_newuserdata(L,sizeof(struct queue_data)); qd->size=size; qd->head=qd->tail=2; g_api->lua_settop(L,size); g_api->lua_pushcclosure(L,queue,size); g_api->lua_pushvalue(L,-1); g_confirm_closure=g_api->lua_ref(L); return 1; }
树型打印一个 table
调试 lua 程序的时候往往想以树的形式打印出一个 table ,下面这个 print_r 函数可以满足要求。
输出:
不用担心循环引用。
local print = print local tconcat = table.concat local tinsert = table.insert local srep = string.rep local type = type local pairs = pairs local tostring = tostring local next = next function print_r(root) local cache = { [root] = "." } local function _dump(t,space,name) local temp = {} for k,v in pairs(t) do local key = tostring(k) if cache[v] then tinsert(temp,"+" .. key .. " {" .. cache[v].."}") elseif type(v) == "table" then local new_key = name .. "." .. key cache[v] = new_key tinsert(temp,"+" .. key .. _dump(v,space .. (next(t,k) and "|" or " " ).. srep(" ",#key),new_key)) else tinsert(temp,"+" .. key .. " [" .. tostring(v).."]") end end return tconcat(temp,"\n"..space) end print(_dump(root, "","")) end
a = {} a.a = { hello = { alpha = 1 , beta = 2, }, world = { foo = "ooxx", bar = "haha", root = a, }, } a.b = { test = a.a } a.c = a.a.hello print_r(a)
输出:
+a+hello+alpha [1] | | +beta [2] | +world+root {.} | +bar [haha] | +foo [ooxx] +c {.a.hello} +b+test {.a}
不用担心循环引用。
在 lua 中实现函数的重载。
下面试一下:
输出:
local function create() local arg_table = {} local function dispatcher (...) local tbl = arg_table local n = select ("#",...) local last_match for i = 1,n do local t = type(select(i,...)) local n = tbl[t] last_match = tbl["..."] or last_match if not n then return last_match (...) end tbl = n end return (tbl["__end"] or tbl["..."])(...) end local function register(desc,func) local tbl = arg_table for _,v in ipairs(desc) do if v=="..." then assert(not tbl["..."]) tbl["..."] = func return end local n = tbl[v] if not n then n = {} tbl[v]=n end tbl = n end tbl["__end"] = func end return dispatcher, register, arg_table end local all={} local function register(env,desc,name) local func = desc[#desc] assert(type(func)=="function") desc[#desc] = nil local func_table if all[env] then func_table = all[env] else func_table = {} all[env] = func_table end if env[name] then assert(func_table[name]) else env[name],func_table[name] = create() end func_table[name](desc,func) end define = setmetatable({},{ __index = function (t,k) local function reg (desc) register(getfenv(2),desc,k) end t[k] = reg return reg end })
下面试一下:
define.test { "number", function(n) print("number",n) end } define.test { "string", "number", function(s,n) print("string number",s,n) end } define.test { "number", "...", function(n,...) print("number ...",n,...) end } define.test { "...", function(...) print("default",...) end } test(1) test("hello",2) test("hello","world") test(1,"hello")
输出:
number 1 string number hello 2 default hello world number ... 1 hello