Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a class table member distinct in Lua objects?

Tags:

lua

Consider the following code:

#!/usr/bin/lua

local field = 
{
    name = '',
    array = {},

    new = function(self, o)
        o = o or {}
        setmetatable(o, self)
        self.__index = self
        return o
    end,
}

local fld1 = field:new()
local fld2 = field:new()

fld1.name = 'hello'
table.insert(fld1.array, 1)
table.insert(fld1.array, 2)
table.insert(fld1.array, 3)

fld2.name = 'me'
table.insert(fld2.array, 4)
table.insert(fld2.array, 5)

print('fld1: name='..fld1.name..' len='..#fld1.array)
print('fld2: name='..fld2.name..' len='..#fld2.array)

The output when executed is as follows:

fld1: name=hello len=5
fld2: name=me len=5

From the output, it can be seen that name has different values in fld1 and fld2. array, however, has the same value in fld1 and fld2 (fld1.array and fld2.array are the same and therefore have the same length of 5).

How do I fix this code so that fld1.array is independent of fld2.array (so that modifying fld1.array does not change fld2.array)?

like image 608
hermit.crab Avatar asked Jan 31 '26 01:01

hermit.crab


1 Answers

First off, fld1 and fld2 have distinct names because you gave them distinct names - their own properties.

When you perform table key assignment the new key and value are stored directly in the table that you specify. The __index metamethod only comes in to play when you perform table key lookup and the key is not found in the table.

A quick example where we can see that table key assignment will shadow keys in the __index lookup chain.

local Foo = { shared = 'shared', private = 'private' }
Foo.__index = Foo

local foo = setmetatable({}, Foo)

foo.private = 'a shadowed value'

print(Foo.shared, foo.shared) -- shared shared
print(Foo.private, foo.private) -- private  a shadowed value

Note: there is a __newindex metamethod for catching table key assignment that involves a never-before-seen key.

Consider treating the new method more like a traditional constructor function, wherein you assign 'private' properties to newly created instances.

local Field = {
    -- shared properties go here
}

-- shared methods are defined as such

function Field:new (name)
    local o = {
        -- private properties for the newly created object go here
        name = name or '',
        array = {}
    }

    self.__index = self

    return setmetatable(o, self)
end

function Field:insert (value)
    table.insert(self.array, value)
end

local fld1 = Field:new('hello')
local fld2 = Field:new('me')

fld1:insert(1)
fld1:insert(2)
fld1:insert(3)

fld2:insert(4)
fld2:insert(5)

print('fld1: name='..fld1.name..' len='..#fld1.array) -- fld1: name=hello len=3
print('fld2: name='..fld2.name..' len='..#fld2.array) -- fld2: name=me len=2

Some more notes:

  • Generally, 'class' names should be in PascalCase, to make them distinct.
  • Having a :new function in the lookup chain means instances can invoke it, this may or may not be desirable. (Can create slightly ugly inheritance this way.)
  • There is a difference between how you define methods which use the implicit self. You should give Chapter 16 of Programming in Lua a read. It might be a tad dated if you're using 5.2 or 5.3, but it should still have a lots of useful information.
  • If you're interested in a tiny library for this, I recently wrote Base. If you look around, you'll find lots of little OOP packages for making these kinds of things a bit easier.
like image 65
Oka Avatar answered Feb 03 '26 05:02

Oka