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

question on metatables

Started by Termanater13, 14 March 2014 - 03:24 PM
Termanater13 #1
Posted 14 March 2014 - 04:24 PM
how do I write a metatable to detect changes in its values? the end product I'm looking for is a way to act upon changes made based on the change done.

say if I'm storing the scale of a monitor in a.scale, how do I detect the change, and change the scale of the monitor? (following code is current iteration of a button API as I learn about metatables)

local button = {
  display = function (self) -- Draw the buttons on screen
	self.mon.clear()
	self.mon.setCursorPos(2,2)
	self.mon.write("X")
  end,
  new = function (self) -- Add button to system

  end,
  delete = function (self) -- removes button from system

  end,
  handleEvents = function (self) -- event handler for the API
	print("test")
  end,
  toggleStatus = function (self) -- toogles the status of the button

  end,
}
local scale = {
  test = function ()
	print("test")
  end
}
function new(side)
  local newInstance = {
	side = side,
	mon = peripheral.wrap(side),
	Data = {},
	size = {},
	scale = 1
  }
  print(newInstance.mon.getSize())
  newInstance.size.x, newInstance.size.y = newInstance.mon.getSize()
  setmetatable(newInstance, {__index = button})
  return newInstance
end
also is it possible to reference a table a metatable is stored in? (required for future Ideas for the code above)
Edited on 14 March 2014 - 03:29 PM
Lyqyd #2
Posted 14 March 2014 - 04:37 PM
You'd use the __newindex metamethod, iirc. You should be able to reference the table as an upvalue if you declare the function after you've declared the table, within the same scope.

Edit: also, I believe the newindex metamethod is passed table, key, newValue, so you can simply use than in your function.
Termanater13 #3
Posted 14 March 2014 - 05:14 PM
so the following should work:

if key == "scale" then
  self.mon.setTextScale(newValue)
  self.scale = newValue
else
  self[key] = newValue
end
but could it cause a infinite loop?
CometWolf #4
Posted 14 March 2014 - 06:23 PM
I've never tried, but i suspect it might. You have to use rawset(table,index,value) to bypass newindex. However, the following function approach is probably more suited, since newindex is only triggered if the index dosen't already exist.

scale = function(self, scale)
  self.mon.setTextScale(scale)
  self.scale = scale
end
Alternatively you would have to use two tables, one to assign the value to, which then passes it on to a second one via newindex. Then uses index to get the value from the second table whenever it's requested.
Edited on 14 March 2014 - 06:56 PM
Termanater13 #5
Posted 15 March 2014 - 05:31 AM
I've never tried, but i suspect it might. You have to use rawset(table,index,value) to bypass newindex. However, the following function approach is probably more suited, since newindex is only triggered if the index dosen't already exist.
 scale = function(self, scale) self.mon.setTextScale(scale) self.scale = scale end 
Alternatively you would have to use two tables, one to assign the value to, which then passes it on to a second one via newindex. Then uses index to get the value from the second table whenever it's requested.
I have already started to implement the function, but I like the second table Idea. my only question is how can I keep some kind of flexibility, like being able to have multiple instances(one for each side for example)
theoriginalbit #6
Posted 15 March 2014 - 06:11 AM
so the following should work:

if key == "scale" then
  self.mon.setTextScale(newValue)
  self.scale = newValue
else
  self[key] = newValue
end
but could it cause a infinite loop?
assuming the code is in the __newindex metamethod then yes, it will cause a recursive loop. to circumvent this you make use of rawset; when attempting to get variables you should use rawget.

I have already started to implement the function, but I like the second table Idea. my only question is how can I keep some kind of flexibility, like being able to have multiple instances(one for each side for example)
Assuming I understand what you're asking here the best way is after wrapping your peripherals looping through them and then replacing the appropriate functions, making sure to reference back to the original monitor object.
Edited on 15 March 2014 - 05:15 AM
Termanater13 #7
Posted 15 March 2014 - 07:12 AM
I have already started to implement the function, but I like the second table Idea. my only question is how can I keep some kind of flexibility, like being able to have multiple instances(one for each side for example)
Assuming I understand what you're asking here the best way is after wrapping your peripherals looping through them and then replacing the appropriate functions, making sure to reference back to the original monitor object.
He talked about referencing a table to store and call the data, and my code uses one variable per side set by the user. I was looking for something like what he was talking about because it will solve the original question.
I know I will need some kind of cross talk ability, but I know that would need to store a reference to a table and not the table as a value, or something like that.
CometWolf #8
Posted 15 March 2014 - 09:48 AM
I was thinking something along these lines. Idk how well it would work, as i've never done it, but it's theoretically sound as far as i can tell.

function new()
  local obj
  obj = setmetatable(
    {
	  values = {
	    --default values go here
	  }
    },
    {
	  __index = obj.values,
	  __newindex = function(t,k,v)
	    --newindex functionality goes here
	    obj.values[k] = v
	  end
    }
  }
  return obj
end