This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
DannySMc's profile picture

Lua tables, on function execute help

Started by DannySMc, 25 May 2016 - 12:02 PM
DannySMc #1
Posted 25 May 2016 - 02:02 PM
Hey guys,
Environments are not my strong point, so I was wondering if I have a table like so:
local object = {
say = function( self, text )
print(text)
return true
end;
count = function( self, limit )
for i=1, limit do
print(i)
end
return true
end;
init = function( self )
-- This will do something
end;
}
This is a basic example, but is there a way so every time a function is run in that table, I can run something just before it is called, example:
So when I run: object:say("hey")
It could maybe (AN EXAMPLE) run the init function inside of the object? So say maybe when I run count
it will run object:init() then start count, with it all being internal, so all I have to do is call the function
and it is all done for me?
Thanks in advance,
Danny
Edited on 25 May 2016 - 12:03 PM
KingofGamesYami #2
Posted 25 May 2016 - 02:09 PM
Hurray for metatables!


setmetatable( your_table, {__index = function( t, k )
  if type( t[ k ] ) == "function" then
    doRandomStuff()
  end
  return rawget( t, k )
end}
DannySMc #3
Posted 25 May 2016 - 04:21 PM
Thanks for the help, what is the raw get for? And will this go anywhere? So where would I get the return for that? In the running function?
Bomb Bloke #4
Posted 25 May 2016 - 04:45 PM
The idea is that when you index into your table to get a function, the __index function will be called instead. That function then uses rawget() to retrieve the function you were trying to call in the first place.

The problem is that __index doesn't work that way… it's only used if the requested key doesn't exist in the table. Meaning that if you try to call a function that is in the table, none of Yami's code will actually do anything.

One way around this is to put the original functions in one table, then build the metatable out of a different one (which is kept entirely empty):

local hiddenObject = {define bunch of functions here}

local object = {}

setmetatable( object , {__index = function( _, key )
  if type( hiddenObject[ key ] ) == "function" and key ~= "init" then
    hiddenObject.init()
  end
  return hiddenObject[ key ]
end}

Now if you do object.say(), indexing into "object" to get "say" will call the __index function, which first calls "hiddenObject.init()" and then returns the "say" function from "hiddenObject" back to the original line - from where it'll be called, as you've got a () sitting directly after the index.

I've no idea if it'd execute faster or not, but another way to achieve much the same effect:

local hiddenObject = {define bunch of functions here}

local object = {init = hiddenObject.init}

for key, value in pairs(hiddenObject) do
  if type(value) == "function" and key ~= "init" then
    object[key] = function(...)
      object.init()
      return value(...)
    end
  end
end
KingofGamesYami #5
Posted 25 May 2016 - 05:35 PM
The problem is that __index doesn't work that way… it's only used if the requested key doesn't exist in the table.

Ugh, I always forget that… until I actually use it, that is.
DannySMc #6
Posted 25 May 2016 - 05:45 PM
The problem is that __index doesn't work that way… it's only used if the requested key doesn't exist in the table.
Ugh, I always forget that… until I actually use it, that is.

Ahh, er hm

The idea is that when you index into your table to get a function, the __index function will be called instead. That function then uses rawget() to retrieve the function you were trying to call in the first place.
The problem is that __index doesn't work that way… it's only used if the requested key doesn't exist in the table. Meaning that if you try to call a function that is in the table, none of Yami's code will actually do anything.
One way around this is to put the original functions in one table, then build the metatable out of a different one (which is kept entirely empty):
local hiddenObject = {define bunch of functions here}
local object = {}
setmetatable( object , {__index = function( _, key )
if type( hiddenObject[ key ] ) == "function" and key ~= "init" then
hiddenObject.init()
end
return hiddenObject[ key ]
end}
Now if you do object.say(), indexing into "object" to get "say" will call the __index function, which first calls "hiddenObject.init()" and then returns the "say" function from "hiddenObject" back to the original line - from where it'll be called, as you've got a () sitting directly after the index.
I've no idea if it'd execute faster or not, but another way to achieve much the same effect:
local hiddenObject = {define bunch of functions here}
local object = {init = hiddenObject.init}
for key, value in pairs(hiddenObject) do
if type(value) == "function" and key ~= "init" then
object[key] = function(...)
object.init()
return value(...)
end
end
end

Hmm interesting, but it seems to be a lot more code than I was hoping for… :/