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

Function return handler

Started by flaghacker, 20 May 2014 - 11:28 AM
flaghacker #1
Posted 20 May 2014 - 01:28 PM
I'm trying to make a API for the glasses (from openperipheral addons). I want to make a function like this:


function createStausBar(data)
   --code here
   return handler
end

a = createStausBar(data)

a.setMaxValue(5)
a.setValue(3)

How can I do this? I assume I will have to go the oop route?
Edited on 20 May 2014 - 11:29 AM
theoriginalbit #2
Posted 20 May 2014 - 01:33 PM
without knowing more context that is perfectly valid code.
flaghacker #3
Posted 20 May 2014 - 01:47 PM
I want to make an API that allows to easily make a status bar, based on coordinates, the maximum value and the current value. I want to make that function return a table containing a function to set the value.
theoriginalbit #4
Posted 20 May 2014 - 02:02 PM
then yes your best route is to make use of metatables :)/>
apemanzilla #5
Posted 20 May 2014 - 02:04 PM
Just define the functions inside the table to be returned

local handler = {}
function handler.draw()
--do stuff
end
return handler
then yes your best route is to make use of metatables :)/>
Of course, OOP is always better ;)/>
Edited on 20 May 2014 - 12:04 PM
theoriginalbit #6
Posted 20 May 2014 - 02:11 PM
Of course, OOP is always better ;)/>
Eh, not always. OOP isn't the only design pattern in the world of programming.
apemanzilla #7
Posted 20 May 2014 - 02:13 PM
Of course, OOP is always better ;)/>
Eh, not always. OOP isn't the only design pattern in the world of programming.
Correction: OOP is usually better (from my experiences)
flaghacker #8
Posted 20 May 2014 - 04:25 PM
Thanks, I'm going to read some oop tutorials!
apemanzilla #9
Posted 20 May 2014 - 04:31 PM
Thanks, I'm going to read some oop tutorials!
Make sure you understand meta tables first :P/>
flaghacker #10
Posted 20 May 2014 - 06:31 PM
Thanks, I'm going to read some oop tutorials!
Make sure you understand meta tables first :P/>
Metawhat?
This: http://www.lua.org/pil/13.html isn't too helpful…
Edited on 20 May 2014 - 04:33 PM
viluon #11
Posted 20 May 2014 - 06:37 PM
Oh yes it is! Have a look at http://www.lua.org/pil/13.4.1.html and http://www.lua.org/pil/16.html
Edited on 20 May 2014 - 04:38 PM
apemanzilla #12
Posted 20 May 2014 - 08:16 PM
Meta tables. Tables about tables. One key example is having one table and setting the "__index" value, which can be another table that it will look in for missing values:

a={}
b={}
b.test = 17
setmetatable(a, {__index=b})
print(a.test)
That will print "17". This is useful for setting the "class" of a table, simply by setting meta tables which provide it with the "class" functions.
flaghacker #13
Posted 21 May 2014 - 06:00 AM
So you can set defauld values for your variables? How do you use this?
CometWolf #14
Posted 21 May 2014 - 07:33 AM
Seeing as a variable might be a function aswell, this is very useful. If you make the first arg of the function the table(obj) to operate on, you can do this aswell

obj:func(args)
and it would be the same as doing

obj.func(obj,args)

Now if this function is in the __index you don't need to define it here aswell.
MKlegoman357 #15
Posted 21 May 2014 - 07:37 AM
You don't really need to use metatables for this:


local function getHandler (text)
  local handler = {
    text = text or "default text"
  }

  handler.getText = function ()
    return handler.text
  end

  handler.setText = function (newText)
    handler.text = newText
  end

  return handler
end

local handle = getHandler("Hello")

print(handle.getText()) -->> Hello

handle.setText("World!")

print(handle.getText()) -->> World!
Edited on 21 May 2014 - 10:10 AM
theoriginalbit #16
Posted 21 May 2014 - 08:43 AM
SpoilerYou don't really need to use metatables for this:


local function getHandler (text)
  local handler = {
	text = text or "default text"
  }

  handler.getText = function ()
	return handler.text
  end

  handler.setText = function (newText)
	handler.text = newText
  end

  return handler
end

local handle = newHandler("Hello")

print(handle.getText()) -->> Hello

handle.setText("World!")

print(handle.getText()) -->> World!
MKlegoman357:17: attempt to call nil

in any case, it is better to use OO for that, you can reuse the functions as opposed to creating a bunch of functions in memory.

OO Example

--# I'm using the do block here to hide the `prototype` table from outside scopes
--# we don't want anything gaining direct access to that, we want them to access it via a handler
local newHandler
do
  local prototype = {}

  function prototype:getText()
    return self.text
  end

  function prototype:setText(text)
    self.text = text
  end

  newHandler = function(text)
    local ivars = {
      text = text or "default"
    }
    return setmetatable(ivars, {__index = prototype})
  end
end

local helloWorld = newHandler("Hello")
local fooBar = newHandler("foo")

print(helloWorld:getText())
print(fooBar:getText())

--# change helloWorld text, fooBar text will stay the same
helloWorld:setText(helloWorld:getText() .. " World!")

print(helloWorld:getText())
print(fooBar:getText())

take note of the use of the colon syntax however. If you wish to use the dot notation you must either do helloWorld.getText(helloWorld) or make use of a handy little script I made here which you would replace the local prototype = {} with. That little script allows you to invoke functions either with the colon syntax or dot notation without having to supply the table (if you do however that is fine).
Edited on 21 May 2014 - 06:59 AM
flaghacker #17
Posted 21 May 2014 - 02:49 PM
SpoilerYou don't really need to use metatables for this:


local function getHandler (text)
  local handler = {
	text = text or "default text"
  }

  handler.getText = function ()
	return handler.text
  end

  handler.setText = function (newText)
	handler.text = newText
  end

  return handler
end

local handle = newHandler("Hello")

print(handle.getText()) -->> Hello

handle.setText("World!")

print(handle.getText()) -->> World!
MKlegoman357:17: attempt to call nil

in any case, it is better to use OO for that, you can reuse the functions as opposed to creating a bunch of functions in memory.

OO Example

--# I'm using the do block here to hide the `prototype` table from outside scopes
--# we don't want anything gaining direct access to that, we want them to access it via a handler
local newHandler
do
  local prototype = {}

  function prototype:getText()
	return self.text
  end

  function prototype:setText(text)
	self.text = text
  end

  newHandler = function(text)
	local ivars = {
	  text = text or "default"
	}
	return setmetatable(ivars, {__index = prototype})
  end
end

local helloWorld = newHandler("Hello")
local fooBar = newHandler("foo")

print(helloWorld:getText())
print(fooBar:getText())

--# change helloWorld text, fooBar text will stay the same
helloWorld:setText(helloWorld:getText() .. " World!")

print(helloWorld:getText())
print(fooBar:getText())

take note of the use of the colon syntax however. If you wish to use the dot notation you must either do helloWorld.getText(helloWorld) or make use of a handy little script I made here which you would replace the local prototype = {} with. That little script allows you to invoke functions either with the colon syntax or dot notation without having to supply the table (if you do however that is fine).

I don't understand the variable part I think…
This is my test code:
Spoiler

local glass = peripheral.wrap("right")
glass.clear()
local frontArgs = { 1, 1, 50, 50, 0xFF0000, 0 }


function createBox(frontArgs)
	local newHandler
	do
		local prototype = {}
		--Variables
		prototype.frontHandler = glass.addBox(unpack(frontArgs))
		--Functions
		function prototype:setWidth(value)
			self.frontHandler.setWidth(value)
		end		
		--Main
		newHandler = function(text)
		local vars =
			{
			text = text or "derp"
			}
		return setmetatable(vars, { __index = prototype })
	  end
	end
end

testHandler = createBox(frontArgs)
testHandler:setWidth(500)

Error:

test:28: attempt to index ? (a nil value)
I assume it doesn't find the setWidth function?

–EDIT:
Derpy text formatting in notepad++, I didn't saw that function at the end, I will try to fix it…

–EDIT2:
Oh you're not supposed to put everything in a function, it is already a function…
Edited on 21 May 2014 - 12:57 PM
theoriginalbit #18
Posted 21 May 2014 - 03:09 PM
–EDIT2:
Oh you're not supposed to put everything in a function, it is already a function…
Indeed.
flaghacker #19
Posted 21 May 2014 - 05:42 PM
Thanks for all the help, I'm liking this! Can you just check this code to see if I'm doing something inefficient/wrong? This code creates a bar at the top left corner and slowly fills it.
http://pastebin.com/Fcz2dzQp
Spoiler

local glass = peripheral.wrap("right")
glass.clear()
local settings =
	{
	x1 = 0,
	y1 = 0,
	x2 = 100,
	y2 = 20,
	z = 1,
	displayColor = 0xFF0000,
	backColor = 0x000000,
	opacity = 100,
	maxValue = 50
	}

local createBox
do
	local prototype = {}
	
	function prototype:initiate(vars)
		self.settings = vars
		self.settings.width = math.abs(self.settings.x1 - self.settings.x2)
		self.settings.height = math.abs(self.settings.y1 - self.settings.y2)
		print("Creating handler with self.settings = "..textutils.serialize(self.settings))
		self.displayHandler = glass.addBox(self.settings.x1, self.settings.y1, 1, self.settings.height, self.settings.displayColor, self.settings.opacity)
		self.displayHandler.setZ(self.settings.z + 1)
		self.backHandler = glass.addBox(self.settings.x1, self.settings.y1, self.settings.width, self.settings.height, self.settings.backColor, self.settings.opacity)
		self.backHandler.setZ(self.settings.z)
	end
	
	function prototype:setValue(value)
		self.settings.value = value
		self.displayHandler.setWidth(self.settings.value/self.settings.maxValue*self.settings.width)
	end

	createBox = function(vars)
		print("Creating box with "..textutils.serialize(vars))
		return setmetatable(vars, { __index = prototype })
	end
end

test = createBox({})
test:initiate(settings)
for i = 1, 50 do
	sleep(1)
	test:setValue(i)
	print(i.."/50")
end

If I do test = nil, are there any traces of the object I created left? (except for the glasses, there I can just do glass.clear())


–EDIT
Updated program, added delete function
Edited on 21 May 2014 - 03:57 PM
flaghacker #20
Posted 24 May 2014 - 09:18 AM
Current code: http://pastebin.com/Fcz2dzQp
My program works, but the display is a little glitchy. I think it has something to do with the rounding. The bar on my screen gets one pixel to large, then back to normal every time the value chenges. This looks horrible… Anyone knows how to fix it?

–EDIT:
Testing with os.pullEvent('key") has shown the following:


The width changes before the x1. I can't figure out how I can fix this…


Got is fixed, just needed to round some temp variables. Updated pastebin.
Edited on 24 May 2014 - 07:47 AM
flaghacker #21
Posted 24 May 2014 - 07:16 PM
I wanted to make a function called setSide(side) to (you guessed it) set the side of the terminal glasses bridge.
Current API code: http://pastebin.com/Fcz2dzQp
Filename: glass
Test program code: http://pastebin.com/ACSCHFv8
Filename: test

It errors with

glass:102: attemp to index ? (a nil value)

Anyone any idea of what's going on? I suppose I don't understand the hole local - global - oop idea…
Edited on 24 May 2014 - 05:17 PM