Log In  

Based off of this code posted by this nice gentleman: https://github.com/jonstoler/class.lua

I made some small adjustments so I could have headache-free classes and inheritance in my project.

classdef = {}

-- default (empty) constructor
function classdef:init(...) end

-- create a subclass
function classdef:extend(obj)
    local obj = obj or {}

    local function copyTable(table, destination)
        local table = table or {}
        local result = destination or {}

        for k, v in pairs(table) do
            if not result[k] then
                if type(v) == "table" and k ~= "__index" and k ~= "__newindex" then
                    result[k] = copyTable(v)
                else
                    result[k] = v
                end
            end
        end

        return result
    end

    copyTable(self, obj)

    obj._ = obj._ or {}

    local mt = {}

    -- create new objects directly, like o = Object()
    mt.__call = function(self, ...)
        return self:new(...)
    end

    -- allow for getters and setters
    mt.__index = function(table, key)
        local val = rawget(table._, key)
        if val and type(val) == "table" and (val.get ~= nil or val.value ~= nil) then
            if val.get then
                if type(val.get) == "function" then
                    return val.get(table, val.value)
                else
                    return val.get
                end
            elseif val.value then
                return val.value
            end
        else
            return val
        end
    end

    mt.__newindex = function(table, key, value)
        local val = rawget(table._, key)
        if val and type(val) == "table" and ((val.set ~= nil and val._ == nil) or val.value ~= nil) then
            local v = value
            if val.set then
                if type(val.set) == "function" then
                    v = val.set(table, value, val.value)
                else
                    v = val.set
                end
            end
            val.value = v
            if val and val.afterSet then val.afterSet(table, v) end
        else
            table._[key] = value
        end
    end

    setmetatable(obj, mt)

    return obj
end

-- set properties outside the constructor or other functions
function classdef:set(prop, value)
    if not value and type(prop) == "table" then
        for k, v in pairs(prop) do
            rawset(self._, k, v)
        end
    else
        rawset(self._, prop, value)
    end
end

-- create an instance of an object with constructor parameters
function classdef:new(...)
    local obj = self:extend({})
    if obj.init then obj:init(...) end
    return obj
end

function class(attr)
    attr = attr or {}
    return classdef:extend(attr)
end

Basically I've changed the bits where it says Class to classdef. The whole thing eats up about 350 tokens, I'm not sure how to go about optimising it as I only have a yellow belt in Lua-fu. I ran it in a cart and it seems to work so far.

I'm posting this because when I looked for advice on defining a class in pico 8, a lot of people were creating copies of functions and stuffing them into tables. Expensive, but easy - maybe too easy. Or people would go the other way and do the setmetatable boilerplate code for every class and extended class. Efficient but ugly.

I've done most of my Lua coding so far using Codea on the iPad. It has a built in class() function that behaves just like the one above. So much easier than trying to understand metatables, and it can sit in its own tab. It also does some wacky getter / setter stuff that I'm not sure I need, but I might abuse it at some point.

If you can point out any immediate problems with the code (as in certain parts will break in pico 8) or optimisations I'd be very grateful. Other class implementations are welcome too.

P#65881 2019-07-16 09:51 ( Edited 2019-07-16 10:10)

if obj.init then obj:init(...) end

do you need the conditional here? unless someone intentionally sets subclass.init=nil, won't obj:init always resolve back up to the default empty classdef.init?

P#72822 2020-02-06 19:39 ( Edited 2020-02-06 19:54)

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2024-04-19 00:50:18 | 0.006s | Q:8