Serialized tables and metamethods

Posted by tobiassjosten on Fri 07 Oct 2005 11:32 AM — 3 posts, 17,892 views.

Sweden #0
Using Nick's serializer in http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=4960 I've been able to save some tables into MUSHclient variables, which is all good and dandy. However, now I want to stick some metamethods on those tables and it just wont work. I haven't got much experience with metatables, besides getting it to work, so I figure there's something I'm missing here.

Currently I have this variable (skoid_cfg) saved with the serializer.
cfg = {}
  cfg["cure_salve"] = true
  cfg["heal_mana"] = true
  cfg["heal_mana_offset"] = 15
  cfg.loot = true
  cfg.timer = true


The "skoid_cfg" variable is loaded with the following code.
if GetVariable("skoid_cfg") then loadstring(GetVariable("skoid_cfg"))() end


Now I want to attach two metamethods to this table. They look like this.
skoid_mt =  { __index = function (t, name) gui_update() end; __newindex = function (t, name, val) gui_update end; }
setmetatable(cfg, skoid_mt)


But lo and behold! No matter where I put the "setmetatable", it wont work.. I tried defining the cfg table before loading it, and setting the metatable right after. But I figure it's all overwritten when loading it (since it's redefined). But since setting the metatable after loading the table wont work neither, I'm out of ideas..

Anyone?
Australia Forum Administrator #1
It worked for me, I tried this in the immediate window:



cfg = {}
  cfg["cure_salve"] = true
  cfg["heal_mana"] = true
  cfg["heal_mana_offset"] = 15
  cfg.loot = true
  cfg.timer = true



skoid_mt =  
  { __index = function (t, name) 
    Note ("__index called for " .. name);
   end; 
   __newindex = function (t, name, val) 
   Note ("__newindex  called for " .. name .. ", value " .. val);
   end; 
  }

setmetatable(cfg, skoid_mt)

Note (cfg.abcd)
cfg.blah = 22



Output


__index called for abcd
nil
__newindex  called for blah, value 22


You know the metamethods are only called for an indexed item that does not exist? If you do something like:


cfg.loot = false


That will not call the metamethod. If you are trying to make it refresh something on access (which you seem to be doing) then you need to make an empty proxy table. This is explained in the Lua book on page 115.

See this for explanation (the online version):


http://www.lua.org/pil/13.4.4.html


The method described there maintains an empty table, and thus every access calls the metamethod. Of course you want the data to be stored/retrieved, so the metamethod does it via the local table.


Amended on Sat 08 Oct 2005 01:33 AM by Nick Gammon
Sweden #2
That explains alot. Could have sworn my tests showed me that the metamethods were called even when changed allready existing keys.. Proxy tables looks neat, I'll have to do some experimenting with them.

Thanks.