//
BaseClass.lua
name1.lua.txt 中:
第一行 local name1 = BaseClass(“name1”)
local function __init(self,…)
end
local function __delete(self,…)
end
name1.__init = __init
name1. __delete = __delete
return name1
local aLuaTemp = require(“a/b/c/name1”)
aLuaTemp.New(…)
aLuaTemp:Delete() --//不能直接接着写Delete(), 要在执行完之后其它地方Delete
--保存类类型的虚表
local _class = {}
-- 自定义类型
ClassType = {
class = 1,
instance = 2,
}
function BaseClass(classname, super)
assert(type(classname) == "string" and #classname > 0)
-- 生成一个类类型
local class_type = {}
-- 在创建对象的时候自动调用
class_type.__init = false
class_type.__delete = false
class_type.__cname = classname
class_type.__ctype = ClassType.class
class_type.super = super
class_type.New = function(...)
-- 生成一个类对象
local obj = {}
obj._class_type = class_type
obj.__ctype = ClassType.instance
-- 在初始化之前注册基类方法
setmetatable(obj, {
__index = _class[class_type],
})
-- 调用初始化方法
do
local create
create = function(c, ...)
if c.super then
create(c.super, ...)
end
if c.__init then
c.__init(obj, ...)
end
end
create(class_type, ...)
end
-- 注册一个delete方法
obj.Delete = function(self)
local now_super = self._class_type
while now_super ~= nil do
if now_super.__delete then
now_super.__delete(self)
end
now_super = now_super.super
end
end
return obj
end
local vtbl = {}
_class[class_type] = vtbl
setmetatable(class_type, {
__newindex = function(t,k,v)
vtbl[k] = v
end
,
--For call parent method
__index = vtbl,
})
if super then
setmetatable(vtbl, {
__index = function(t,k)
local ret = _class[super][k]
--do not do accept, make hot update work right!
--vtbl[k] = ret
return ret
end
})
end
return class_type
end
//------------- 使用动态生成例子
LuaA.lua.txt
中在父挂点 动态生成几个子挂点,每个子挂点上的lua脚本LuaB.lua.txt一样,只是LuaB上的对应的数据 是各是各的数据,
在LuaA中如下:
for k, v in pairs(aaaaaa) then
local aLuaTemp = require(“a/b/c/name1”)
local aTemp = aLuaTemp.New(…) – 类似实例化东西
table.insert(luaScripts, aTemp)
--aLuaTemp:Delete() --//不能直接接着写Delete(), 要在执行完之后其它地方Delete
end
然后再遍历luaScripts 进行调用LuaB中的SetToDo方法。
在LuaB中如下:
local luaBItem = BaseClass(‘luaBItem’)
local CurItemData = {}
local function __init(self, paramA, paramB, paramC) – 注意第一个参数self
self. CurItemData = paramA – 赋值属于自己的data
end
local function __delete(self)
self. CurItemData = nil
end
local function SetToDo (self)
用的时候 self. CurItemData 是属于自己的Data, 如果不带self,这个CurItemData是属于LuaB脚本的,动态多个创建循环赋值的,类似c#闭包的表现
end
luaBItem.__init = __init
luaBItem.__delete = __delete
luaBItem.SetToDo = SetToDo
return luaBItem
/
rawget是为了绕过__index而出现的,直接点,就是让__index方法的重写无效。(我这里用到"重写"二字,可能不太对,希望能得到纠正)
Window = {}
Window.prototype = {x = 0 ,y = 0 ,width = 100 ,height = 100,}
Window.mt = {}
function Window.new(o)
setmetatable(o ,Window.mt)
return o
end
Window.mt.__index = function (t ,key)
return 1000
end
Window.mt.__newindex = function (table ,key ,value)
if key == "wangbin" then
rawset(table ,"wangbin" ,"yes,i am")
end
end
w = Window.new{x = 10 ,y = 20}
print(rawget(w ,w.wangbin))
打印结果是:nil。这里的元表中__index函数就不再起作用了。
但是rawset呢,起什么作用呢?我们再来运行一段代码。
Window = {}
Window.prototype = {x = 0 ,y = 0 ,width = 100 ,height = 100,}
Window.mt = {}
function Window.new(o)
setmetatable(o ,Window.mt)
return o
end
Window.mt.__index = function (t ,key)
return 1000
end
Window.mt.__newindex = function (table ,key ,value)
table.key = "yes,i am"
end
w = Window.new{x = 10 ,y = 20}
w.wangbin = "55"
然后我们的程序就stack overflow了。可见,程序陷入了死循环。因为w.wangbin这个元素本来就不存在表中,然后这里不断执行进入__newindex,陷入了死循环。
//
Lua中的异常处理pcall、xpcall、debug使用实例
如果需要在Lua中处理错误,必须使用函数pcall(protected call)来包装需要执行的代码。
pcall接收一个函数和要传递个后者的参数,并执行,执行结果:有错误、无错误;返回值true或者或false, errorinfo
if pcall(function_name, ….) then
-- no error
else
-- some error
end
简单示例
> =pcall(function(i) print(i) end, 33)
33
true
> =pcall(function(i) print(i) error('error..') end, 33)
33
false stdin:1: error..
这里注意对返回值的逻辑判断
> function f() return false,2 end
> if f() then print '1' else print '0' end
0
pcall以一种"保护模式"来调用第一个参数,因此pcall可以捕获函数执行中的任何错误。
通常在错误发生时,希望落得更多的调试信息,而不只是发生错误的位置。但pcall返回时,它已经销毁了调用桟的部分内容。Lua提供了xpcall函数,xpcall接收第二个参数——一个错误处理函数,当错误发生时,Lua会在调用桟展看(unwind)前调用错误处理函数,于是就可以在这个函数中使用debug库来获取关于错误的额外信息了。
debug库提供了两个通用的错误处理函数:
debug.debug:提供一个Lua提示符,让用户来价差错误的原因
debug.traceback:根据调用桟来构建一个扩展的错误消息
>=xpcall(function(i) print(i) error('error..') end, function() print(debug.traceback()) end, 33)
33
stack traceback:
stdin:1: in function <stdin:1>
[C]: in function 'error'
stdin:1: in function <stdin:1>
[C]: in function 'xpcall'
stdin:1: in main chunk
[C]: in ?
false nil
/// Lua中的metatable, setmetatable getmetatable详解
Lua 中 metatable 是一个普通的 table,但其主要有以下几个功能:
1.定义算术操作符和关系操作符的行为
2.为 Lua 函数库提供支持
3.控制对 table 的访问
Metatables 定义操作符行为
Metatable 能够被用于定义算术操作符和关系操作符的行为。例如:Lua 尝试对两个 table 进行加操作时,它会按顺序检查这两个 table 中是否有一个存在 metatable 并且这个 metatable 是否存在 __add 域,如果 Lua 检查到了这个 __add 域,那么会调用它,这个域被叫做 metamethod。
Lua 中每个 value 都可以有一个 metatable(在 Lua 5.0 只有 table 和 userdata 能够存在 metatable)。每个 table 和 userdata value 都有一个属于自己的 metatable,而其他每种类型的所有 value 共享一个属于本类型的 metatable。在 Lua 代码中,通过调用 setmetatable 来设置且只能设置 table 的 metatable,在 C/C++ 中调用 Lua C API 则可以设置所有 value 的 metatable。默认的情况下,string 类型有自己的 metatable,而其他类型则没有:
复制代码代码如下:
print(getmetatable('hi')) --> table: 003C86B8
print(getmetatable(10)) --> nil
Metamethod 的参数为操作数(operands),例如:
复制代码代码如下:
local mt = {}
function mt.__add(a, b)
return 'table + ' .. b
end
local t = {}
setmetatable(t, mt)
print(t + 1)
每个算术操作符有对应的 metamethod:
+ | __add |
* | __mul |
- | __sub |
/ | __div |
- | __unm (for negation) |
% | __mod |
^ | __pow |
对于连接操作符有对应的 metamethod:__concat
同样,对于关系操作符也都有对应的 metamethod:
== | __eq |
< | __lt |
<= | __le |
其他的关系操作符都是用上面三种表示:
a ~= b 表示为 not (a == b)
a > b 表示为 b < a
a >= b 表示为 b <= a
和算术运算符不同的是,关系运算符用于比较拥有不同的 metamethod(而非 metatable)的两个 value 时会产生错误,例外是比较运算符,拥有不同的 metamethod 的两个 value 比较的结果是 false。
不过要注意的是,在整数类型的比较中 a <= b 可以被转换为 not (b < a),但是如果某类型的所有元素并未适当排序,此条件则不一定成立。例如:浮点数中 NaN(Not a Number)表示一个未定义的值,NaN <= x 总是为 false 并且 x < NaN 也总为 false。
为 Lua 函数库提供支持
Lua 库可以定义和使用的 metamethod 来完成一些特定的操作,一个典型的例子是 Lua Base 库中 tostring 函数(print 函数会调用此函数进行输出)会检查并调用 __tostring metamethod:
复制代码代码如下:
local mt = {}
mt.__tostring = function(t)
return '{' .. table.concat(t, ', ') .. '}'
end
local t = {1, 2, 3}
print(t)
setmetatable(t, mt)
print(t)
另外一个例子是 setmetatable 和 getmetatable 函数,它们定义和使用了 __metatable 域。如果你希望设定的 value 的 metatable 不被修改,那么可以在 value 的 metatable 中设置 __metatable 域,getmetatable 将返回此域,而 setmetatable 则会产生一个错误:
复制代码代码如下:
mt.__metatable = "not your business"
local t = {}
setmetatable(t, mt)
print(getmetatable(t)) --> not your business
setmetatable(t, {})
stdin:1: cannot change protected metatable
看一个完整的例子:
复制代码代码如下:
Set = {}
local mt = {}
function Set.new(l)
local set = {}
-- 为 Set 设置 metatable
setmetatable(set, mt)
for _, v in ipairs(l) do set[v] = true end
return set
end
function Set.union(a, b)
-- 检查 a b 是否都是 Set
if getmetatable(a) ~= mt or getmetatable(b) ~= mt then
-- error 的第二个参数为 level
-- level 指定了如何获取错误的位置
-- level 值为 1 表示错误的位置为 error 函数被调用的位置
-- level 值为 2 表示错误的位置为调用 error 的函数被调用的地方
error("attempt to 'add' a set with a not-set value", 2)
end
local res = Set.new{}
for k in pairs(a) do res[k] = true end
for k in pairs(b) do res[k] = true end
return res
end
function Set.intersection(a, b)
local res = Set.new{}
for k in pairs(a) do
res[k] = b[k]
end
return res
end
mt.__add = Set.union
mt.__mul = Set.intersection
mt.__tostring = function(s)
local l = {}
for e in pairs(s) do
l[#l + 1] = e
end
return '{' .. table.concat(l, ', ') .. '}'
end
mt.__le = function(a, b)
for k in pairs(a) do
if not b[k] then return false end
end
return true
end
mt.__lt = function(a, b)
return a <= b and not (b <= a)
end
mt.__eq = function(a, b)
return a <= b and b <= a
end
local s1 = Set.new({1, 2, 3})
local s2 = Set.new({4, 5, 6})
print(s1 + s2)
print(s1 ~= s2)
控制 table 的访问
__index metamethod
在我们访问 table 的不存在的域时,Lua 会尝试调用 __index metamethod。__index metamethod 接受两个参数 table 和 key:
复制代码代码如下:
local mt = {}
mt.__index = function(table, key)
print('table -- ' .. tostring(table))
print('key -- ' .. key)
end
local t = {}
setmetatable(t, mt)
local v = t.a
__index 域也可以是一个 table,那么 Lua 会尝试在 __index table 中访问对应的域:
复制代码代码如下:
local mt = {}
mt.__index = {
a = 'Hello World'
}
local t = {}
setmetatable(t, mt)
print(t.a) --> Hello World
我们通过 __index 可以容易的实现单继承(类似于 JavaScrpit 通过 prototype 实现单继承),如果 __index 是一个函数,则可以实现更加复杂的功能:多重继承、caching 等。我们可以通过 rawget(t, i) 来访问 table t 的域 i,而不会访问 __index metamethod,注意的是,不要太指望通过 rawget 来提高对 table 的访问速度(Lua 中函数的调用开销远远大于对表的访问的开销)。
__newindex metamethod
如果对 table 的一个不存在的域赋值时,Lua 将检查 __newindex metamethod:
1.如果 __newindex 为函数,Lua 将调用函数而不是进行赋值
2.如果 __newindex 为一个 table,Lua 将对此 table 进行赋值
如果 __newindex 为一个函数,它可以接受三个参数 table key value。如果希望忽略 __newindex 方法对 table 的域进行赋值,可以调用 rawset(t, k, v)
结合 __index 和 __newindex 可以实现很多功能,例如:
1.OOP
2.Read-only table
3.Tables with default values
Read-only table
复制代码代码如下:
function readOnly(t)
local proxy = {}
local mt = {
__index = t,
__newindex = function(t, k, v)
error('attempt to update a read-only table', 2)
end
}
setmetatable(proxy, mt)
return proxy
end
days = readOnly{'Sun', 'Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat'}
print(days[1])
days[2] = 'Noday' --> stdin:1: attempt to update a read-only table
有时候,我们需要为 table 设定一个唯一的 key,那么可以使用这样的技巧:
复制代码代码如下:
local key = {} -- unique key
local t = {}
t[key] = value
/// Lua中的metatable, setmetatable getmetatable详解
local obj = {}
setmetatable( obj, { __index = mission_t } )
return obj
mission_t为对应模块的文件名。
在LUA中的文件中使用module("mission_t",package.seeall )这样的形式,可以防止某些命名的冲突,相当于C++的namespace,在调用这个模块的函数的时候,加上模块名:函数名就可以了。
如果要继承某个模块,使用setmetatable( obj,{ __index = XXXX } )
XXXX为希望继承的模块名。
总结:用lua进行面向对象的编程,声明方法和调用方法统一用冒号,对于属性的调用全部用点号
///
-----------------------------------
-----------lua面向对象------------
-----------------------------------
-----------基本函数------------
-- People = {}
-- function People.sayHi()
-- print("Hi, nice to meet you.")
-- end
-- People.sayHi();
-- --Hi, nice to meet you.
-- People.sayHi = function()
-- print("Hi, nice to meet you from func")
-- end
-- People.sayHi();
-- --Hi, nice to meet you from func
-----------基本函数------------
-----------表的遍历------------
-- local arr = {"one", "two", ["one"] = "xxx", ["two"] = "yyy"}
-- local intValue = 3
-- for key, val in pairs(arr) do
-- print("AA value of "..key.." is "..val)
-- end
-- --输出table所有的属性,包括“数组”属性
-- --value of 1 is one
-- --value of 2 is two
-- --value of one is xxx
-- --value of two is yyy
-- for key, val in ipairs(arr) do --友情介绍另外一个函数 ipairs
-- print("BB value of "..key.." is "..val)
-- end
-- --只输出table的“数组”属性
-- --value of 1 is one
-- --value of 2 is two
-- for key, val in pairs(intValue) do
-- print("CCvalue of "..key.." is "..val)
-- end
-- -- 不是table 在调用pairs函数的时候跑出异常
-- -- bad argument #1 to 'pairs' (table expected, got number)
-----------表的遍历------------
-----------工具函数 Clone------------
-- function clone(origin)
-- local dest = {}
-- for key, value in pairs(origin) do
-- dest[key] = value
-- end
-- return dest;
-- end
-- --定义类
-- People = {}
-- People.sayHi = function()
-- print("Hi, nice to meet you from func")
-- end
-- -- --直接用clone 调用 用类生产对象,对象有了类的方法
-- -- p = clone(People)
-- -- p.sayHi();
-- ------包装一层 new, 实际调用clone函数返回
-- function People.new()
-- self = clone(People)
-- return self
-- end
-- p = People.new()
-- p.sayHi();
-- --Hi, nice to meet you from func
-----------工具函数 Clone------------
-----------工具函数 带参数的new------------
-- function clone(origin)
-- local dest = {}
-- for key, value in pairs(origin) do
-- dest[key] = value
-- end
-- return dest;
-- end
-- People = {}
-- People.sayHi = function(self)
-- print("Hi, nice to meet you from func, My name is "..self._name)
-- end
-- function People.new(name)
-- self = clone(People)
-- self._name = name
-- return self
-- end
-- p = People.new("Steven")
-- --p.sayHi() ---- attempt to index local 'self' (a nil value)(标记调用的函数 self._name 是nil)
-- --p.sayHi(p) -----Hi, nice to meet you from func, My name is Steven
-- p:sayHi() -----Hi, nice to meet you from func, My name is Steven
-- -----用冒号(:)代替点号(.),lua会自动帮我们变形,将点号前面的对象传递给参数。
-- -----例如 lilei:setGirlFriend("hanmeimei") - - ->lilei.setGirlFriend(lilei, "hanmeimei")
-----------工具函数 带参数的new------------
-----------工具函数 继承------------
-- 工具函数
function clone(origin)
local dest = {}
for key, value in pairs(origin) do
dest[key] = value
end
return dest;
end
--定义类
People = {}
People.sayHi = function(self)
print("Hi, nice to meet you from func, My name is "..self._name)
end
People.eat = function()
print("Different people eat different thing")
end
People.new = function (name)
self = clone(People)
self._name = name
return self
end
function copy(dest, tab)
for key, val in pairs(tab) do
dest[key] = val
end
end
-- 定义子类
Chinese = {}
Chinese.new = function(name, sheng)
self = People.new(name)--new 父类对象实现继承
self._sheng = sheng
copy(self, Chinese)-- 子类上面的 特有的属性也附着到self上面
return self
end
Chinese.eat = function()-- 覆盖父类方法
print("Eat Mantou")
end
Chinese.buyHouse = function()
print("Can not afford at all")
end
Steven = Chinese.new ("Steven", "henan")
print(Steven._name.." is from "..Steven._sheng) -- 父类的成员和自己的成员
Steven:sayHi() -- 继承父类方法
Steven:eat() -- 覆盖父类方法
Steven:buyHouse() -- 子类独有的方法
-- Steven is from henan
-- Hi, nice to meet you from func, My name is Steven
-- Eat Mantou
-- Can not afford at all
-----------工具函数 继承------------
-----------------------------------
-----------lua面向对象------------
-----------------------------------
--------------------
这里提供 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
_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() -- 定义一个基类 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
=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 中的成员函数。
=test.new(1) -- 输出两行,base_type ctor 和 test ctor 。这个对象被正确的构造了。
a:print_x() -- 输出 1 ,这个是基类 base_type 中的成员函数。
a:hello() -- 输出 hello test ,这个函数被重载了。