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

oop strange set of local variable

Started by BlankWolf, 07 June 2014 - 12:25 AM
BlankWolf #1
Posted 07 June 2014 - 02:25 AM
Hello everyon,

I am trying to add some functions to an object when I call the function "createNewSubScreen". It seems to work an I can call these functions with my object.
Unfortunately somethin in the added function "setPosition" went wrong.

class
Spoiler

--[[ LOCAL FUNCTIONS ]]--
local function trim(_value)
	return tostring(_value):gsub('^%s*(.-)%s*$', '%1')
end

local function empty(_value)
	if (type(_value) == 'nil' or trim(_value) == '') then
		return true
	end
	return false
end

local function checkValue(_value, _type, _empty)
	if (_empty and not empty(_value) and type(_value) ~= _type) then
		error('expected "' .. _type .. '", "nil", or "empty string" got "' .. type(_value) ..'"', 3)
	elseif (type(_value) ~= _type) then
		error('expected "' .. _type .. '" got "' .. type(_value) ..'"', 3)
	end
end

--[[ SCREEN CLASS ]]--
local monitorlist = {}
function new(_name)
	checkValue(_name, 'string')
	if (monitorlist[_name]) then
		error('peripheral allready exists', 2)
	end
	
	local private = {}
	local self = {}
	
	if (_name:find('^(child)')) then
		_name = tostring(_name):gsub('^(child)(.-)$', '%2')
		private.child = true
	else
		width, height = term.getSize()
		private.position = {
			width = width;
			height = height;
		}
	end
	
	private.name = _name
	
	-- CREATE SUBSCREEN
	function self.newSubScreen(_name)
		if (not private.childs) then
			private.childs = {}
		end
		checkValue(_name, 'string')
		if (private.childs[_name]) then
			error('subScreen allready exists', 2)
		end
		
		local subscreen = new ('child' .. _name)
		
		subscreen.setParent = function(_parent) private.parent = _parent end
		subscreen.setParent(self)
		subscreen.setParent = nil
		
		-- ADD SUBSCREEN FUNCTIONS
		function subscreen.setPosition(_posX, _posY, _width, _height)
			checkValue(_posX, 'number', true)
			checkValue(_posY, 'number', true)
			checkValue(_width, 'number', true)
			checkValue(_height, 'number', true)
			
			if (empty(_posX) or empty(_posY) or empty(_width) or empty(_height)) then
				_posX, _posY, _width, _height = nil, nil, nil, nil
			end
			
			private.position = {
				posX = _posX;
				posY = _posY;
				width = _width;
				height = _height;
			}
			
			return subscreen
		end
		
		function subscreen.getParent()
			return private.parent
		end
		
		function subscreen.getSiblingByName(_name)
			checkValue(_name, 'string')
			return private.parent.getSubScreenByName(_name)
		end
		
		function subscreen.getSiblingByPosition(_posX, _posY)
			checkValue(_posX, 'number')
			checkValue(_posY, 'number')
			return private.parent.getSubScreenByPosition(_posX, _posY)
		end
		
		function subscreen.getPosition()
			if (private.position) then
				return private.position
			end
			return nil
		end
		
		private.childs[_name] = subscreen
		return subscreen
	end
	
	-- FUNCTIONS
	
	function self.getName()
		return private.name
	end
	
	function self.getSubScreenByName(_name)
		checkValue(_name, 'string')
		
		if (private.childs[_name]) then
			return private.childs[_name]
		end
		return nil
	end
	
	function self.getSubScreenByPosition(_posX, _posY)
		checkValue(_posX, 'number')
		checkValue(_posY, 'number')
		
		for name, obj in pairs(private.childs) do
			position = obj.getPosition()
			for n, v in pairs(position) do
				print('DEBUG::' .. n .. ' => ' .. v)
			end
			if (position and position.posX and position.posY and position.width and position.height) then
				if (_posX >= position.posX and _posX < position.posX + position.width and _posY >= position.posY and _posY < position.posY + position.height) then
					--return obj
				end
			end
		end
		
		return nil
	end
	
	-- RETURN
	if (not private.child) then
		monitorlist[_name] = self
	end
	return self
end

test code
Spoiler

term.clear()
term.setCursorPos(1, 1)

os.unloadAPI('monitor')
os.loadAPI('monitor')

mon1 = monitor.new('monitor_0')
sub1 = mon1.newSubScreen('sub_0').setPosition(1, 1, 10, 10)


print(sub1.getName())
for k, n in pairs(sub1.getPosition()) do
	print(k .. ' => ' .. n)
end

sub2 = mon1.newSubScreen('sub_1').setPosition(1, 11, 10, 10)

print("\n" .. sub1.getName())
for k, n in pairs(sub1.getPosition()) do
	print(k .. ' => ' .. n)
end

sub3 = mon1.newSubScreen('sub_2').setPosition(1, 21, 10, 10)

print("\n" .. sub1.getName())
for k, n in pairs(sub1.getPosition()) do
	print(k .. ' => ' .. n)
end

output
Spoiler

sub_0
posX => 1
width => 10
height => 10
posY => 1

sub_0
posX => 1
width => 10
height => 10
posY => 11

sub_0
posX => 1
width => 10
height => 10
posY => 21

As you can see in the output, every time when I call the function "setPosition" in an other object then "sub1" the position of sub1 get also changed.
I can't figure out why that happens and hope that someone can follow me and see where my mistak is.

BlankWolf
Bomb Bloke #2
Posted 07 June 2014 - 03:28 AM
To the best of my understanding, Lua does not allow an "object" to reference itself without aid. If you want that to happen, you have to pass it the reference manually.

Say you want your subscreen to alter its position. You thus need to pass the subscreen object to the setPosition function when you call it, and that function needs to apply its changes to that object specifically - even if that function is a child of the object itself.

Eg, in very loose terms:

function someSubscreen.setPosition(self, _posX, _posY, _width, _height)
 self.posX = _posX;
 self.posY = _posY;
 self.width = _width;
 self.height = _height;
end

someSubscreen.setPosition(someSubscreen, 10,2,5,4)

You can condense this a bit, but it comes with a change of syntax:

function someSubscreen:setPosition(_posX, _posY, _width, _height)
 self.posX = _posX;
 self.posY = _posY;
 self.width = _width;
 self.height = _height;
end

someSubscreen:setPosition(10,2,5,4)

I recommend a read through this article.
BlankWolf #3
Posted 07 June 2014 - 11:06 AM
Thanks for your answer.

function someSubscreen.setPosition(self, _posX, _posY, _width, _height)
self.posX = _posX;
self.posY = _posY;
self.width = _width;
self.height = _height;
end

someSubscreen.setPosition(someSubscreen, 10,2,5,4)
But if I chang my code like that (it may work I don't test it) tha attributes "posX", "posY", "width", "height" are not private, so everyone can access them directly.
In your opinien thats maybe fine, but for me thats not an option. They have to be unaccessible.

If have changed my testcode a bit.
Spoiler

term.clear()
term.setCursorPos(1, 1)

os.unloadAPI('monitor')
os.loadAPI('monitor')

mon1 = monitor.new('monitor_0')
mon2 = monitor.new('monitor_1')
sub11 = mon1.newSubScreen('sub_1')
sub1 = mon1.newSubScreen('sub_0').setPosition(1, 1, 10, 10)

print(sub11.getName())
print(sub1.getName())
for k, n in pairs(sub1.getPosition()) do
	print(k .. ' => ' .. n)
end
print(sub1.getParent().getName())

sub2 = mon2.newSubScreen('sub_1').setPosition(1, 11, 10, 10)
print("\n" .. sub1.getName())
for k, n in pairs(sub1.getPosition()) do
	print(k .. ' => ' .. n)
end
print(sub1.getParent().getName())

and here is the output for that
Spoiler

sub_1
sub_0
posX => 1
width => 10
height => 10
posY => 1
monitor_0

sub_0
posX => 1
width => 10
height => 10
posY => 1
monitor_0

When I look at the ouput from my testcode, it seems to me, that every subscreen object from an monitor change their positions, when the function "setPosition" is called.
The objects from other monitors are not effectet by this.
theoriginalbit #4
Posted 07 June 2014 - 11:20 AM
based on you stating this

But if I change my code like that (it may work I don't test it) tha attributes "posX", "posY", "width", "height" are not private, so everyone can access them directly.
you may want to take a look at this API.
Bomb Bloke #5
Posted 08 June 2014 - 04:06 AM
But if I chang my code like that (it may work I don't test it) tha attributes "posX", "posY", "width", "height" are not private, so everyone can access them directly.

That wasn't what I was suggesting, but forget what I was saying anyway.

I guess all you want to do is rig things so that this:

                        private.position = {
                                posX = _posX;
                                posY = _posY;
                                width = _width;
                                height = _height;
                        }

… works rather more like this:

                        private[_name].position = {
                                posX = _posX;
                                posY = _posY;
                                width = _width;
                                height = _height;
                        }

… along with similar changes to the rest of your subscreen functions.
BlankWolf #6
Posted 08 June 2014 - 01:12 PM
Well thanks for to all for your help.

But I figuerd out an other soulution. Instead off adding the functions in the way i did, i wrote it in the if where I check for an subscreen name.

Spoiler

   if (_name:find('^(child)')) then
		_name = _name:gsub('^(child)(.-)$', '%2')
		private.child = true
		
		-- ADD SUBSCREEN FUNCTIONS
		function self.setParent(_parent)
			if (_parent) then
				private.parent = _parent
				self.setParent = nil
			end
		   return self
		end
		
		function self.setPosition(_posX, _posY, _width, _height)
			checkValue(_posX, 'number', true)
			checkValue(_posY, 'number', true)
			checkValue(_width, 'number', true)
			checkValue(_height, 'number', true)
			
			if (empty(_posX) or empty(_posY) or empty(_width) or empty(_height)) then
				_posX, _posY, _width, _height = nil, nil, nil, nil
			end
			
			private.position = {
				posX = _posX;
				posY = _posY;
				width = _width;
				height = _height;
			}
			
			return self
		end
		
		function self.getParent()
			return private.parent
		end
		
		function self.getSiblingByName(_name)
			checkValue(_name, 'string')
			return private.parent.getSubScreenByName(_name)
		end
		
		function self.getSiblingByPosition(_posX, _posY)
			checkValue(_posX, 'number')
			checkValue(_posY, 'number')
			return private.parent.getSubScreenByPosition(_posX, _posY)
		end
		
		function self.getPosition()
			if (private.position) then
				return private.position
			end
			return nil
		end
	else
		width, height = term.getSize()
		private.position = {
			width = width;
			height = height;
		}
	end

Now it works. So Topic can be closed. :D/>
Edited on 08 June 2014 - 11:15 AM