Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Getter

I'm trying to figure out getters in Codea. Testing the approache from stackoverflow http://stackoverflow.com/questions/10578935/lua-getters-and-setters but it doesn't work.

I have a test code like this

Foo = class()

function Foo:greeting()
    return "My name is"
end

function setup()
    local obj = Foo()
    print(obj.greeting(), obj.name)
end

It works just fine giving an output like

My name is nil

But if I add before setup() a function

function Foo:__index(key)
    if key == "name" then
        return "foo"
    else
        return rawget(self, key)
    end
end

I'm getting an error like "attempt to call field 'greeting' (a nil value)"

So I got lost. How should I implement getters in Codea?

Comments

  • dave1707dave1707 Mod
    Posts: 7,528

    I'm not sure what your trying to do, but here's an example.

    http://nova-fusion.com/2011/04/04/implementing-proper-gettersetters-in-lua/
    
  • Posts: 11

    Thank you. Of course I saw this article as it's the first in googling this subject. But it doesn't work for me either. I get nil error or stack overflow error (when using "self.class.__classDict" instead of "Test.__classDict").

    And my point is to write a simple getter (and setter) for a property - nothing more. If you'll run my code you'll see the problem. And I got really stuck with it.

    Does anybody use getters in Codea? Could you share some example code please?

  • dave1707dave1707 Mod
    Posts: 7,528

    @Iavmax I haven't the slightest idea of what your trying to do, but I took your code and was playing with it. If I take the () off the line print( obj.greeting,obj.name) I don't get an error, but I'm not sure what result I'm supposed to get. Maybe this will help some.


    Foo = class() function Foo:greeting() return "My name is" end function setup() local obj = Foo() print(obj.greeting, obj.name) end function Foo:__index(key) if key == "name" then return "foo" else return rawget(self, key) end end
  • dave1707dave1707 Mod
    Posts: 7,528

    @Iavmax Like I said above, I don't know what you're trying to do but here's what I found out playing with the code. Apparently by using __index, you're reading a table. That table is the self values that get created in Foo: init(). It doesn't look like anything other than __index gets called using obj. If I run this code, I think it prints what you're after. Also, when you hit the return rawget code in the function Foo:__index(key), it calls Foo__index again and goes into an infinite loop and causes an error.


    function setup() local obj = Foo() print(obj.greeting, obj.name) end Foo = class() function Foo:init() self.greeting="My name is" end function Foo:__index(key) if key == "name" then return "foo" else return rawget(self.key) end end
  • dave1707dave1707 Mod
    Posts: 7,528

    @Iavmax I also found this. Again, not sure if this is what you're after but it seems to allow you to set values and read them back.


    function setup() foo = MyClass() foo.testMember = 5 foo.testMember = 2 print( foo.testMember ) end MyClass = class() function MyClass:init() self.members = {} end function MyClass:__newindex( index, value ) if index == "testMember" then self.members[index] = value print( "Set member " .. index .. " to " .. value ) else rawset( self, index, value ) end end function MyClass:__index( index ) if index == "testMember" then print( "Getting " .. index ) return self.members[index] else return rawget( self, index ) end end
  • dave1707dave1707 Mod
    Posts: 7,528

    @Iavmax I just realized the code I have above is the same code in your link from your first post. The code I show above seems to work if I understand getters and setters correctly. So I'm not sure why you say in your first post that it doesn't work.

  • edited January 2015 Posts: 1,976

    @lavmax Why do you even want to make getters and setters? They're usually only used in OOP, when you have a private variable that other classes can't reach without a getter/setter. In Lua, there are no private variables, and you could access any variable from any class. The only place I could see you needing this is if you have a variable that is local to a tab, but then, couldn't you just simply have 2 functions, one setting the variable, and the other returning it?

  • Posts: 11

    @dave1707 The problem is that your code (and mine) is not working. Just add any function to your class and you'll get an error. Like this


    function setup() foo = MyClass() foo.testMember = 5 foo.testMember = 2 print( foo.testMember ) foo:foo() end MyClass = class() function MyClass:init() self.members = {} end function MyClass:foo() print("functions work too") end function MyClass:__newindex( index, value ) if index == "testMember" then self.members[index] = value print( "Set member " .. index .. " to " .. value ) else rawset( self, index, value ) end end function MyClass:__index( index ) if index == "testMember" then print( "Getting " .. index ) return self.members[index] else return rawget( self, index ) end end

    And who needs a class without functions. Theoretically there should be a property __classDict that holds class functions, but in Codea it is nil. So may be @Simeon could bring some light into the subject.

    @SkyTheCoder I need it for dynamic properties, that are calculated, not stored. Of course I can use c++ style with getter/setter functions, but it's very ugly and uncommon in languages like lua.

  • dave1707dave1707 Mod
    edited January 2015 Posts: 7,528

    @Iavmax I understand what you're saying now.

  • toffertoffer Mod
    edited January 2015 Posts: 151

    @Iavmax When you create a class, you create the metatable of the following instances. In your example, the method foo is linked to the metatable of the instance foo (foo = MyClass). In the __index getter, self refer to the instance, not it's metatable. A silly fix to illustrate:

    function MyClass:__index( index )
       if index == "testMember" then
          print( "Getting " .. index )
          return self.members[index]
       else
          if index == "foo" then
             return rawget(getmetatable(self), index)
          end
          return rawget( self, index )
       end
       --[[ or handle all case with
       else
             return rawget(getmetatable(self), index) or rawget( self, index )
       end]]
    end
    
  • dave1707dave1707 Mod
    Posts: 7,528

    @Iavmax Here's a way that I came up with. It does the __index and __newindex methods and also any class function. I added some examples of using the index and newindex methods. Not sure if this is what you're after, but I'm learning about metatables.


    function setup() z=xx() -- create instance of xx z:update(10,12345) -- update key 10 with value 12345 z:read(10) -- read key 10 z:read(2) -- read key 2, error, key doesnt exist z:update(24,10) -- update key 24 with value 10, value out of range for key z:test() -- call function test end xx=class() function xx:init() self.tab1={} -- table for values self.tab2={} -- table for meta methods setmetatable(self.tab1,self.tab2) -- create meta function for table read self.tab2.__index= function(tab,key) local r=rawget(tab,key) -- read table if r==nil then -- entry not found return "key not found" else return r end end -- create meta function for table update self.tab2.__newindex= function(tab,key,val) if key==24 and (val<20 or val>30) then -- set limit for key print("key "..key,"value "..val.." out of range, not updated") else rawset(tab,key,val) -- update table end end end function xx:update(k,v) print("update key "..k.." with value "..v) self.tab1[k]=v end function xx:read(k) print("read key "..k.." ",self.tab1[k]) end function xx:test() print("the test function was called") end
  • edited January 2015 Posts: 11

    @toffer Thank you! I think I'm starting to get it. My next question could seem strange but why do we need

    return = rawget(self, index)
    

    if __index itself is called only when there is no such key in an object?

    @dave1707 Thank you for your example.

  • toffertoffer Mod
    edited January 2015 Posts: 151

    @lavmax you're right, I've blended the way I usualy implement getters with your example (a single case property lookup). So to respect the property lookup chain, something like this should be better

    function MyClass:__index( index )
        return self.members[index] or getmetatable(self)[index]
    end
    
  • Posts: 11

    @toffer Got it, finally. Thank you so much!

Sign In or Register to comment.