Lua 中使用面向对象(续)--newproxy生成userdata使用__gc

来源:互联网 发布:java pfx证书读取私钥 编辑:程序博客网 时间:2024/05/16 07:34

   转自http://www.cnblogs.com/yaukey/p/4568202.html

   上一篇文章给了一个面向对象的方案,美中不足的是没有析构函数 Destructor,那么这一次就给它加上。

  既然是析构,那么就是在对象被销毁之前做该做的事情,lua 5.1 的 userdata 可以给其 metatable 增加一个 __gc 域,指定一个函数,将会在被回收时调用,这个 __gc 只能用于 userdata,普遍的 table 不支持;到了 lua 5.2 以后,官方支持了给予普通 table 的 metatable 增加 __gc 域,可以在回收前被回调;具体细节可以参考对应版本的 manual。

  userdata 一般是 c 里创建的自定义数据结构,但是如果想在 lua 里做这件事情的该如何实现,理论上 lua 是不支持的,但是作者增加了一个隐藏的非公开测试函数 newproxy 用于创建一个空的 userdata,参数可以选择是否带 metatable。用法如下:

[cpp] view plain copy
  1. newproxy<span></span><strong>--如果参数设置为true则返回一个userdate带一个空的metatable,如果参数一个newproxy产生的userdata,  
  2.          则返回一个共用传入参数的metatable的userdata。如果参数是false,返回一个没有metatable的userdata  
  3. </strong>newproxy (boolean or proxy)  
  4. Undocumented feature of Lua.  
  5. Arguments: boolean - returned proxy has metatable or userdata - different proxy created with newproxy  
  6. Creates a blank userdata with an empty metatable, or with the metatable of another proxy. Note that, in ROBLOX, creating   
  7. a proxy with another proxy is disabled and will error.  
  8.   
  9. local a = newproxy(true)   
  10. local mt = getmetatable(a)   
  11. print( mt ~= nil )  
  12.    
  13. local b = newproxy(a)   
  14. print( mt == getmetatable(b) )  
  15.    
  16. local c = newproxy(false)   
  17. print( getmetatable(c) ~= nil )  
  18.    
  19. mt.__index = {Name="Proxy"}   
  20. print( a.Name )  
  21. print( b.Name )  
  22.   
  23. -- Output:  
  24. true  
  25. true  
  26. false  
  27. Proxy  
  28. Proxy  

  使用它就可以创建一个空的 userdata 并指定 __gc 操作,在你的对象上保持一个唯一的引用到该 userdata,当你的对象被销毁前 userdata 的 __gc 会被调用。

  对于 5.2 及以后版本的 table 的 __gc,需要注意,你必须在第一次为其设置 metatable 时就制定 __gc,才能开启改标记,否则先设置 metatable,而到其后修改 metatable,增加 __gc 域,是不起作用的。

  下面给出改进过的面向对象方案,注意这里做了版本区分,TxClass:

[cpp] view plain copy
  1. -- Get current version number.  
  2. local _, _, majorv, minorv, rev = string.find(_VERSION, "(%d).(%d)[.]?([%d]?)")  
  3. local VersionNumber = tonumber(majorv) * 100 + tonumber(minorv) * 10 + (((string.len(rev) == 0) and 0) or tonumber(rev))  
  4.   
  5. -- Declare current version number.  
  6. TX_VERSION = VersionNumber  
  7. TX_VERSION_510 = 510  
  8. TX_VERSION_520 = 520  
  9. TX_VERSION_530 = 530  
  10.   
  11. -- The hold all class type.  
  12. local __TxClassTypeList = {}  
  13.   
  14. -- The inherit class function.  
  15. local function TxClass(TypeName, SuperType)  
  16.     -- Create new class type.  
  17.     local ClassType = {}  
  18.   
  19.     -- Set class type property.  
  20.     ClassType.TypeName = TypeName  
  21.     ClassType.Constructor = false  
  22.     ClassType.SuperType = SuperType  
  23.   
  24.     -- The new alloc function of this class.  
  25.     ClassType.new = function (...)  
  26.         -- Create a new object first and set metatable.  
  27.         local Obj = {}  
  28.   
  29.         -- Give a tostring method.  
  30.         Obj.ToString = function (self)  
  31.             local str = tostring(self)  
  32.             local _, _, addr = string.find(str, "table%s*:%s*(0?[xX]?%x+)")  
  33.             return ClassType.TypeName .. ":" .. addr  
  34.         end  
  35.   
  36.         -- Do constructor recursively.  
  37.         local CreateObj = function (Class, Object, ...)  
  38.             local Create  
  39.             Create = function (c, ...)  
  40.                 if c.SuperType then  
  41.                     Create(c.SuperType, ...)  
  42.                 end  
  43.   
  44.                 if c.Constructor then  
  45.                     c.Constructor(Object, ...)  
  46.                 end  
  47.             end  
  48.   
  49.             Create(Class, ...)  
  50.         end  
  51.   
  52.         -- Do destructor recursively.  
  53.         local ReleaseObj = function (Class, Object)  
  54.             local Release  
  55.             Release = function (c)  
  56.                 if c.Destructor then  
  57.                     c.Destructor(Object)  
  58.                 end  
  59.   
  60.                 if c.SuperType then  
  61.                     Release(c.SuperType)  
  62.                 end  
  63.             end  
  64.   
  65.             Release(Class)  
  66.         end  
  67.   
  68.         -- Do the destructor by lua version.  
  69.         if TX_VERSION < TX_VERSION_520 then  
  70.             -- Create a empty userdata with empty metatable.  
  71.             -- And mark gc method for destructor.  
  72.             local Proxy = newproxy(true)  
  73.             getmetatable(Proxy).__gc = function (o)  
  74.                 ReleaseObj(ClassType, Obj)  
  75.             end  
  76.   
  77.             -- Hold the one and only reference to the proxy userdata.  
  78.             Obj.__gc = Proxy  
  79.   
  80.             -- Set metatable.  
  81.             setmetatable(Obj, {__index = __TxClassTypeList[ClassType]})  
  82.         else  
  83.             -- Directly set __gc field of the metatable for destructor of this object.  
  84.             setmetatable(Obj,   
  85.             {  
  86.                 __index = __TxClassTypeList[ClassType],  
  87.   
  88.                 __gc = function (o)  
  89.                     ReleaseObj(ClassType, o)  
  90.                 end  
  91.             })  
  92.         end  
  93.   
  94.         -- Do constructor for this object.  
  95.         CreateObj(ClassType, Obj, ...)  
  96.         return Obj  
  97.     end  
  98.   
  99.     -- Give a ToString method.  
  100.     ClassType.ToString = function (self)  
  101.         return self.TypeName  
  102.     end  
  103.   
  104.     -- The super class type of this class.  
  105.     if SuperType then  
  106.         ClassType.super = setmetatable({},   
  107.         {  
  108.             __index = function (t, k)  
  109.                 local Func = __TxClassTypeList[SuperType][k]  
  110.                 if "function" == type(Func) then  
  111.                     t[k] = Func  
  112.                     return Func  
  113.                 else  
  114.                     error("Accessing super class field are not allowed!")  
  115.                 end  
  116.             end  
  117.         })  
  118.     end  
  119.   
  120.     -- Virtual table  
  121.     local Vtbl = {}  
  122.     __TxClassTypeList[ClassType] = Vtbl  
  123.    
  124.     -- Set index and new index of ClassType, and provide a default create method.  
  125.     setmetatable(ClassType,  
  126.     {  
  127.         __index = function (t, k)  
  128.             return Vtbl[k]  
  129.         end,  
  130.   
  131.         __newindex = function (t, k, v)  
  132.             Vtbl[k] = v  
  133.         end,  
  134.   
  135.         __call = function (self, ...)  
  136.             return ClassType.new(...)  
  137.         end  
  138.     })  
  139.    
  140.     -- To copy super class things that this class not have.  
  141.     if SuperType then  
  142.         setmetatable(Vtbl,  
  143.         {  
  144.             __index = function (t, k)  
  145.                 local Ret = __TxClassTypeList[SuperType][k]  
  146.                 Vtbl[k] = Ret  
  147.                 return Ret  
  148.             end  
  149.         })  
  150.     end  
  151.    
  152.     return ClassType  
  153. end  

  使用也很简单:

[cpp] view plain copy
  1. local MyBase = TxClass("MyBase")  
  2. function MyBase:Constructor()  
  3.     print("MyBase:Constructor")  
  4. end  
  5.   
  6. function MyBase:Destructor()  
  7.     print("MyBase:Destructor")  
  8. end  
  9.   
  10. local MyNew = TxClass("MyNew", MyBase)  
  11.   
  12. function MyNew:Constructor()  
  13.     print("MyNew:Constructor")  
  14. end  
  15.   
  16. function MyNew:Destructor()  
  17.     print("MyNew:Destructor")  
  18. end  
  19.   
  20. local cls = MyNew()  
  21. cls = nil  
  22. collectgarbage()  
  23.   
  24. -- Output:  
  25. MyBase:Constructor  
  26. MyNew:Constructor  
  27. MyNew:Destructor  
  28. MyBase:Destructor  

   接下来的扩展是,给一个简单的运行时方法:IsA。

原创粉丝点击