Lua中 面向对象 概念 和 class相关 和 Lua的作用域 和 xpcall 安全调用lua函数

//

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中处理错误,必须使用函数pcallprotected 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),但是如果某类型的所有元素并未适当排序,此条件则不一定成立。例如:浮点数中 NaNNot 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 为一个 tableLua 将对此 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 ,这个函数被重载了。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值