lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]



Vijay Aswadhati wrote:

I have been studying some of the examples such as
"UserDataWithPointerExample"
on the Wiki as well as some real code like the gdbm binding by LHF.

I tried LHF's approach and found out obviously it works as well, however
I am unable to figure out what exactly is the magic behind the statement
"gdbm.metatable.__index=gdbm"

Regarding __metatable, Adam has explained it very well. My example will work without it. I only added it so that one cannot alter the userdata's real metatable from a Lua script. The Lua function getmetatable will return the table of methods instead of the real metatable.

All Lua function are written in the Lua C API, and they are fairly easy to understand. If you look at lua-5.0/src/lib/lbaselib.c you'll see:

static int luaB_getmetatable (lua_State *L) {
  luaL_checkany(L, 1);
  if (!lua_getmetatable(L, 1)) {
    lua_pushnil(L);
    return 1;  /* no metatable */
  }
  luaL_getmetafield(L, 1, "__metatable");
return 1; /* returns either __metatable field (if present) or metatable */
}

static int luaB_setmetatable (lua_State *L) {
  int t = lua_type(L, 2);
  luaL_checktype(L, 1, LUA_TTABLE);
  luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
                    "nil or table expected");
  if (luaL_getmetafield(L, 1, "__metatable"))
    luaL_error(L, "cannot change a protected metatable");
  lua_settop(L, 2);
  lua_setmetatable(L, 1);
  return 1;
}


I don't have a current copy of lgdm.tar.gz, and the site is down, so I cannot explain LHF's code right now. But it appears that he puts the real metatable in the table of methods, and the index event uses the table of methods. Something like:

gdbm = {}  -- table of methods
gdbm.metatable = { __index = gdbm }  -- real metatable

It's hard to explain without diagrams.


The metatable magic is the same for Lua tables as it is for userdata, so it's best to experiment with Lua tables first.

Play around with this example first.

$ cat x.lua

local methods = {
  show = function(t, ...) return t, t.x, t.y, unpack(arg) end,
  dot  = function(t1, t2) return t1.x * t2.x + t1.y * t2.y end,
  common = 'this is common',
}

local mt = {
  __index = methods,
  __tostring = function(t) return 'this object is '.. t.name end,
}

obj1 = { name = 'bob', x=0, y=2 }
setmetatable(obj1, mt)

obj2 = { name = 'bill', x=4, y=3 }
setmetatable(obj2, mt)

$ lua -lx -i
> = obj1, obj1.common
this object is bob      this is common
>
> = obj2, obj2.common
this object is bill     this is common
>
> = obj1:show('a','b')
this object is bob      0       2       a       b
>
> = obj2:show'cow'
this object is bill     4       3       cow
>
> = obj1:dot(obj2)
6
>

Then read:
http://lua-users.org/lists/lua-l/2003-05/msg00287.html

I hope this helps.  Let us know if you have more questions.

There isn't a lot of magic to it. Once you have a mental image of how it works, it's pretty straight forward. I started to make some diagrams but then I got lazy. I'll try to finish them.

- Peter