5

The Lua PIL and Luajit FFI tutorial gave two usages of __index in the metatable.

One is for indexing like obj[123], e.g.,

__index = function (self, k) return self._data+(k-self._lower)

The other usage is to define named methods, as given in the tutorial,

__index = { area = function(a) return a.x*a.x + a.y*a.y end, },

We can then make function call like obj:area().

Can I do both at the same time, e.g., direct indexing and named methods?

Liang
  • 1,015
  • 1
  • 10
  • 13
  • 1
    Yes. You need to check `type(k)` and do the appropriate action (either action for numeric key or action for string key). – Egor Skriptunoff Oct 29 '18 at 18:52
  • Yes, this is the key, and perhaps works cleaner if combined with the answer given by Azdle, that is, to look for string key actions in another metatable. Thank you, Egor Skriptunoff. – Liang Oct 30 '18 at 13:48

1 Answers1

4

The answer, as is usual for extra-interesting code in Lua, is more metatables.

When your __index metamethod is actually a table, Lua simply does a standard table access on the given table. This means you can set a metatable on your metatable. Then you can set an __index metamethod on this "meta-metatable".

foo = function()
  print("foo")
end

bar = function(_, key)
  return function()
    print(string.format("bar: %s", key))
  end
end

mmt = { __index = bar }
mti = { foo = foo }
mt = { __index =  mti }
t = {}

setmetatable(mti, mmt)
setmetatable(t, mt)

t.foo()  -- prints: "foo"
t.bar()  -- prints: "bar: bar"
t.baz()  -- prints: "bar: baz"

With this, when you try to access a field which is absent in both tables, lua will first try to access the top-level table which will access the first metatable which will then call your metamethod in the second metatable.


There is also another, possibly more straight forward, answer: Use your __index metamethod to check another table for named fields:

foo = function()
  print("foo")
end

f = { foo = foo }

bar = function(_, key)
  if f[key] then return f[key] end
  return function()
    print(string.format("bar: %s", key))
  end
end

mt = { __index =  bar }
t = {}

setmetatable(t, mt)

t.foo()  -- prints: "foo"
t.bar()  -- prints: "bar: bar"
t.baz()  -- prints: "bar: baz"

Tested on Lua 5.3.

azdle
  • 2,139
  • 4
  • 17
  • 23
  • Thank you Azdle. This solves my problem. I added codes like `t={1,2,3}` and can then access the content like `t[1]` as well as other named methods using your code. – Liang Oct 30 '18 at 13:53