Problem With Class Functions Accessing "self"

hello fellow renoisers,

while coding on my first tool for renoise, i came across some problems regarding the use of classes.

the first problem is, that, in certain circumstances, an instance cannot access ‘self’.

case 1: accessing instance from a table:

Click to view contents
  
class "TestClass"  
  
function TestClass:__init()  
 self._value = "Hello from an instance of TestClass!"  
end  
  
function TestClass:get_value()  
 return self._value  
end  
  
--------------------  
  
instances = {TestClass()}  
  
-- raises an error:  
-- *** main.lua:8: attempt to index local 'self' (a nil value)  
-- *** stack traceback:  
-- *** main.lua:8: in function 'get_value'  
-- *** main.lua:20: in main chunk  
print (instances[1].get_value())  
  

workaround: passing the instance a reference to itself.
it works, but isn’t very clean imho. furthermore, it raised another question: i thought using “self._variable” generates a private property.
while oprint() does not show such a property, it still is accessable from outside the instance.

Click to view contents
  
class "TestClass"  
  
function TestClass:__init()  
 self._value = "Hello from an Instance of TestClass!"  
end  
  
function TestClass:get_value(instance)  
 return instance._value  
end  
  
--------------------  
  
test_classes = {TestClass()}  
  
-- outputs "Hello from an instance of TestClass!"  
print (test_classes[1]:get_value(test_classes[1]))  
  
-- why is this possible?  
-- outputs "Goodbye private data!"  
test_classes[1]._value = "goodbye private data!"  
print (test_classes[1]._value)  
  

case 2: accessing instance from a table inside an instance of a different class.
basically the same as the first case, but it was how i ran into the problem, so i post it anyway.
when i ran into it, i got the same error message as in the first case. however, the example i wrote gets me a different error message and i am not sure why. the workaround is the same though, so i guess the cause is more or less the same.

Click to view contents
  
class "TestClass1"  
  
function TestClass1:__init()  
 self._value = "Hello from an instance of TestClass!"  
end  
  
function TestClass1:get_value()  
 return self._value  
end  
  
class "TestClass2"  
  
function TestClass2:__init()  
 --  
end  
  
function TestClass2:get_value_from_other_class_instance(instances)  
 return instances[1]:get_value()  
end  
  
--------------------  
  
instances = {TestClass1(), TestClass2()}  
  
-- raises an error:  
-- *** main.lua:18: attempt to index local 'instances' (a nil value)  
-- *** stack traceback:  
-- *** main.lua:18: in function 'get_value_from_other_class_instance'  
-- *** main.lua:30: in main chunk  
print (instances[2].get_value_from_other_class_instance(instances))  
  

workaround:

Click to view contents
  
class "Class1"  
  
function Class1:__init()  
 self._value = "Hello from an instance of Class1!"  
end  
  
function Class1:get_value(instance)  
 return instance._value  
end  
  
class "Class2"  
  
function Class2:__init()  
 --  
end  
  
function Class2:get_value_from_other_instance(instance)  
 return instance:get_value(instance)  
end  
  
--------------------  
  
instances = {Class1(), Class2()}  
  
-- outputs "Hello from an instance of Class1!"  
print (instances[2]:get_value_from_other_instance(instances[1]))  
  
-- why is this possible?  
-- outputs "Goodbye private data!"  
instances[1]._value = "Goodbye private data!"  
print (instances[1]._value)  
  

case 3: same as case 2, though without tables.
i just post it to show which things i tried. the workaround is the same again, so i won’t post it another time. ;)

Click to view contents
  
class "TestClass"  
  
function TestClass:__init()  
 self._value = "Hello from an instance of TestClass!"  
end  
  
function TestClass:get_value()  
 return self._value  
end  
  
function TestClass:get_value_from_other_instance(class_instance)  
 return class_instance:get_value()  
end  
  
--------------------  
  
instance_1 = TestClass()  
instance_2 = TestClass()  
  
-- raises an error:  
-- *** main.lua:12: attempt to index local 'class_instance' (a nil value)  
-- *** stack traceback:  
-- *** main.lua:12: in function 'get_value_from_other_instance'  
-- *** main.lua:25: in main chunk  
print (instance_1.get_value_from_other_instance(instance_2))  
  

so the question now is: did i do something here that should not be done, thus this behavior is on purpose, or is this a bug?

since my tool relies on tables of instances and the interaction between different kinds of classes, this is an important thing to me. although i have a workaround, i don’t want to use it, if there is another solution to it. if it is a bug and the solving of it is “in reach”, then i could just wait a bit until i proceed developing. otherwise, i would just use the workaround and live with the “uncleanliness”.

also the fact that a hidden property (instance._property) can be accessed from outside the instance raises the question if this is just the nature of the class implementation and cannot be changed, or if just something got overlooked or if i don’t understand the concept.

thanks a lot,
Blue Reaction

Hello,

must admit that this confused the hell out of me as well when starting to get in touch with Lua.

To call a method in Lua, use the colon operator:

-> obj = SomeClass()
-> obj:some_method()

which is syntactic sugar for
-> obj.some_method(obj)

Same applies to method defs:
SomeClass:get_value()
– is the same as
SomeClass.get_value(self)

See also http://www.lua.org/pil/16.html#ObjectSec

Especially confusing is that properties do not need the colon.
-> obj.some_property

On a second though this makes sense though, because functions in Lua are first class functions, so a function is a property too, thus it’s invoked with the . operator.

See also luabind lua classes static non static for a few more infos about Renoises’s class impl

thanks for the fast answer,

i feel a bit stupid now not realizing the problem was calling the function with “.” and not “:”.
thank you very much! my code gets a bit complex now and i am really tired. tried hard to spot the
problem yesterday night, to no avail. now realizing that i indeed mixed them up, even in my examples…
actually i already knew that i should use “:” for functions and “.” for properties, but i still
didn’t properly use it in every case.

Blue Reaction

EDIT: maybe you can delete that topic, doesn’t really fit in here anyway, since it’s not a bug and
a rather trivial problem and solution…