lua-users home
lua-l archive

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


Hi list,

I've had recently a problem in tht same category than that of Paul and was about to ask you when I've seen his mail (see copy below).

I was porting an old C++/Java school project to Lua, and it contains a vector3 class representing a position/vector in 3d space. Since I'm using Lua 5.1 since the day it's out, I thought it would have been nice to use the # operator to get the length of the vector. My vector is a table with x, y and z fields, and I don't care about the length of the table itself. My problem is that I then figured out that the # operator is not overridable for tables, since it already exists. I thought it was a bug of some kind, but the Lua source seems to be explicit about that:
[lvm.c:509]
      case OP_LEN: {
        const TValue *rb = RB(i);
        switch (ttype(rb)) {
          case LUA_TTABLE: {
            setnvalue(ra, cast_num(luaH_getn(hvalue(rb))));
            break;
          }
          case LUA_TSTRING: {
            setnvalue(ra, cast_num(tsvalue(rb)->len));
            break;
          }
          default: {  /* try metamethod */
            Protect(
              if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN)) // <- :NOTE: TM_LEN stands for __len
                luaG_typeerror(L, rb, "get length of");
            )
          }
        }
        continue;
      }
[/lvm.c]
If the object is a string or a table the metatable method is not checked at all. Metatable operators are not overridable ones but definable ones (meaning they are used if there is no default). But there's an exception for index and newindew, which are really overridable, there is even special functions to get their default behavior (rawget, rawset). (:NOTE: I haven't tested other operators since table.__len is the only one redefinable in lua that has a default meaning)

That would be really cool to make something more coherent with all that stuff. My proposition would be to make *all* operators overridable, and to provide default versions as system methods, with unified names bases on the event name (rawindex, rawnewindex, rawlen, ...). This would not break *most* of existing code since there is no reason to make an operator override for types which don't call them (__len in a table metatable for example). However it would be a (maybe significative) performance hit, but I think the advantages provided are worth it.

Doub.

-----Message d'origine-----
De : lua-bounces@bazar2.conectiva.com.br [mailto:lua-bounces@bazar2.conectiva.com.br] De la part de Paul Chiusano
Envoyé : 24 mars 2006 12:41
À : Lua list
Objet : Metatables for primitives performance?

I was playing around with primitive metatables. The following works in 5.1:

local function factorial(n)
  return n < 3 and n or n*factorial(n-1) end Number = { factorial = factorial } debug.setmetatable(3, {__index=Number})

Now I can do (7):factorial() and voila! returns 5040! And it seems that I can pass any number I want into debug.setmetatable, with the same effect. Pretty snazzy.

My question is: is there a performance penalty associated with doing this? Does adding this metatable cause all numeric operations to perform slower? Also, I notice that overriding any of the operator metamethods (like __div) for numbers has no effect. Is this for performance reasons?

If you could override the operators for primitives, seems like you'd need rawdiv, rawadd, and so on. For example:

Number = {
  __div = function(num, denom)
    return denom ~= 0 and (num / denom) or error("Divide by zero!")
  end
}

num / denom would just call the same metamethod, leading to an infinite loop.

-Paul